<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7611869677320175211</id><updated>2011-11-27T16:29:06.067-08:00</updated><category term='testing bitching'/><category term='documentation'/><category term='documentation bitching management'/><title type='text'>tristan is a code monkey</title><subtitle type='html'>Notes from an American programmer just kicking it in Vienna.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>23</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-5645234488475198421</id><published>2009-02-01T02:23:00.000-08:00</published><updated>2009-02-01T02:43:13.968-08:00</updated><title type='text'>Good Automated Testing</title><content type='html'>Funny, how after I rant on unit testing a bit, the &lt;a href="http://www.joelonsoftware.com/items/2009/01/31.html"&gt;Stack Overflow podcast&lt;/a&gt; also has a rant about unit testing. But I want to make this clear: I like automated tests. A lot. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I work at a company now that has a whole lot of software that is only manually validated. This has caused nothing but regression after regression. Without calculating anything, I would easily bet anyone, even to odds like 10000:1, that if we get a majority of the code called, which means &gt; 50%, through automated tests, it will save the company tremendous money. And I mean, the amount of time getting the tests written will save the company money over the long term. If you have to see the neverending story of our bug list, you'll know what I'm talking about.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Making tests count is the problem. These "unit-oriented TDD mocks" that want small little tests on everything seem to be working on a problem I've never had to write. That is: a set of services where most of the code manipulates a database. This can be seen like a gigantic global side-effect system. You write code, and you tweak this other thing, outside of the confines of your process' RAM. What this means is each unit test really has to integrate with a database-oriented test. Which doesn't really fit the definition of "unit" test, because sometimes you have:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Code and schemas.&lt;/li&gt;&lt;li&gt;Often more than one DB element.&lt;/li&gt;&lt;li&gt;Often, code that mirrors the schema, and code that stores it.&lt;/li&gt;&lt;/ol&gt;So how do you define &lt;span class="Apple-style-span" style="font-style: italic;"&gt;unit&lt;/span&gt;, as in &lt;span class="Apple-style-span" style="font-style: italic;"&gt;one, isolated, individual, thingy&lt;/span&gt;? I throw this definition away. What I really want is &lt;span class="Apple-style-span" style="font-style: italic;"&gt;automated testing&lt;/span&gt;", because I just don't want people in the process. I'm thinking about saying &lt;span class="Apple-style-span" style="font-style: italic;"&gt;automated component testing&lt;/span&gt;, because I also want the software to be described at the level where you might&lt;span class="Apple-style-span" style="font-style: italic;"&gt; redistribute or reuse it&lt;/span&gt; - that should be your component.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My personal testing-oriented list of principles has become:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Define your components, and test the interaction at this level.&lt;/li&gt;&lt;li&gt;Cover as much as you can automatically easily.&lt;/li&gt;&lt;li&gt;If you have code that can not be called via your component interface, you probably don't need it.&lt;/li&gt;&lt;li&gt;Remove brittle tests. &lt;/li&gt;&lt;li&gt;Avoid dependencies on specialized configuration.&lt;/li&gt;&lt;li&gt;Make configuration painless.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I'm sure this list will be refined over time.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-5645234488475198421?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/5645234488475198421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=5645234488475198421' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5645234488475198421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5645234488475198421'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2009/02/good-automated-testing.html' title='Good Automated Testing'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8854564150310272938</id><published>2009-01-29T11:47:00.000-08:00</published><updated>2009-01-29T12:00:47.314-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='documentation'/><title type='text'>Read, and I mean read, your documentation</title><content type='html'>It must suck being a technical documentation writer. Nobody reads your crap. Don't try to convince me of this otherwise. I've written enough planning and specification and API documentation to learn the Cardinal Rule of Technical Documentation: it will not be read. Skimmed, maybe, but not read.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At fault is the culture of technology. We live in a world of instant connection many thousands of instant messages, microblogs, wall notices, etc. But the display of the computer is still not good enough for general reading. And animation is that wonderful brain candy that everyone loves to hide everywhere in your display, distracting you from your train of thought at a moment's notice.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For code monkeys, this is a sin. Why? Because our documentation tools are usually paltry, clunky affairs to begin with. Take &lt;a href="http://java.sun.com/j2se/javadoc/"&gt;javadoc&lt;/a&gt;, for example. Creating javaDoc is a horrible writing experience. &lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;It uses HTML, which really was never intended to be read, so your documentation is actually not really readable in it's natural state, the text file itself.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Almost every text editor I know does not allow you to create a readable measure in the source code file itself. You end up having to redistribute newlines, etc, yourself.&lt;/li&gt;&lt;li&gt;The default stylesheets of javadoc are, well, horrendous by any kind of typographic standard.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;You add on top of this the technology tools of distraction, and sprinkle in code completion, and voila - nobody will read your API documentation. At least never in detail. And most of the time, not even to get things compiling.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Do yourself a favor. Read documentation you get thoroughly. And I mean truly read it. Explore it, find links, make a meta map. When you know a library well, you will not only start to use it, you will actually think instead of waiting for the compiler to kick in. You will memorize details you never thought possible. You will become an expert, rather than just another dude with a keyboard cranking out the lines like an IDE addict. &lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8854564150310272938?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8854564150310272938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8854564150310272938' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8854564150310272938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8854564150310272938'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2009/01/read-and-i-mean-read-your-documentation.html' title='Read, and I mean read, your documentation'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8203844698571290484</id><published>2009-01-18T14:56:00.000-08:00</published><updated>2009-01-18T14:59:22.281-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing bitching'/><title type='text'>Tristan Can Rant About Testing Like Everyone</title><content type='html'>Once upon a time, I tried to develop unit tests for some code running on a JBoss server. There was a mishmash of crap dependencies, with various calls to Hibernate (a framework I now loathe), lots of wacky static method calls, the list goes on. I barely got the thing running, and it took forever to initialize (well, like 15 seconds, which is insane for a unit test). And then it broke because someone tweaked the ant build file. And because I'm already bald, well, it's ok, I had no hair to pull out.&lt;br /&gt;&lt;br /&gt;The development of this unit test gave me two observations:&lt;br /&gt;&lt;br /&gt;1. Mocking DB requests are useless, especially with a persistence framework like Hibernate.&lt;br /&gt;2. Our internal design sucked balls, and that was 99% of the reason developing a unit test was painful.&lt;br /&gt;&lt;br /&gt;In the end, if you make testing easy, it will be fun, and you will write less code, and all will be well in the universe. The trick is making it easy.&lt;br /&gt;&lt;br /&gt;Here's a few things I've done to start liking testing a bit more:&lt;br /&gt;&lt;br /&gt;1. Get the testing pain out of the way. Use a CI server (Hudson) to drive the slow-ass tests. Keep them running, but don't piss your fellow developers off by having these kinds of tests running at any point during a standard build procedure. I once knew this guy who forced a maven build to run an insane amount of nitpicky testing whenever you tried to *build the main component*. In the end, I now spit upon his name whenever I see his handiwork on the screen.&lt;br /&gt;&lt;br /&gt;2. Start writing things in Scala. (We're a Java shop, please don't hold it against me.) You could use Groovy, or Ruby here. I just like Scala, because I just understand it at a performance level. But the big win is a massive SLOC reduction. Less code wins.&lt;br /&gt;&lt;br /&gt;3. Get as much stuff covered with as few lines of test code as possible. Often, this runs counter to the whole "unit testing" philosophy, but I don't care. Often, this drives me to re-architect code: change how exception handling happens, for example, because you realize that all this error handling happens in the middle of a call stack. It's a way of code spelunking. Over time, I start to refine the conventions I keep, and then break the tests up a bit more.&lt;br /&gt;&lt;br /&gt;In my experience, testing often just gets the same short stick that documentation gets. I'm not simply talking about code comments, but just communication of "what got done". More code monkeys go the route of writing something like "crap happened" on a trouble ticket, and then making a ton of changes with no described rationale. Or they go into useless rambles where you end up skipping over the really important 5th sentence in the 4th paragraph of their diatribe.&lt;br /&gt;&lt;br /&gt;Testing well is in some way like writing well. Elegant testing is fantastic, but it often takes several revisions for you to get there. The whole "unit tests are always great" nonsense reminds me of coders that would say crap like "for every line of code you should have a comment line". ... huh? what?&lt;br /&gt;&lt;br /&gt;&amp;lt;/rant&amp;gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8203844698571290484?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8203844698571290484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8203844698571290484' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8203844698571290484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8203844698571290484'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2009/01/tristan-can-rant-about-testing-like.html' title='Tristan Can Rant About Testing Like Everyone'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-1044716719146912949</id><published>2008-12-13T06:34:00.000-08:00</published><updated>2008-12-13T08:07:07.813-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='documentation bitching management'/><title type='text'>Documented?</title><content type='html'>So, I'm taking over the technical management of a web service. One of the first things I did was talk to everyone, mostly to check the temperature. The kinds of questions I asked were basic. If you ask a basic question, you get a better answer. And one of those questions was &lt;em&gt;What can we do better?&lt;/em&gt; The one unanimous issue was that we needed better documentation.&lt;br /&gt;&lt;br /&gt;Great. Only two problems with this.&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Most of the team doesn't write well.&lt;br /&gt;&lt;/li&gt;&lt;li&gt; In the past, nobody has gone out of their way to really make documentation happen.&lt;/li&gt;&lt;/ol&gt;Why I point this out: most teams probably think that this is just a scheduling problem. As if you just added some time for &lt;em&gt;Documentation&lt;/em&gt;, the problem is solved. I think if you just added more time for documentation, people will just half-ass some crap on the wall and probably spend the rest of the time you slated for documentation surfing the intertubes.&lt;br /&gt;&lt;br /&gt;I need a strategy. I need a system that will help bad writers get better incrementally. Which means that everything must be painfully obvious. The first pieces of the puzzle are the stuff that actually exists, which are the most important parts, anyway.&lt;ul&gt;&lt;li&gt; Code/API Documentation — What does your code do? Especially, what side effects are there?&lt;/li&gt;&lt;li&gt; Adminsitration Guide — How do we run the system?&lt;/li&gt;&lt;li&gt; Users Guide — How do users run the system?&lt;/li&gt;&lt;/ul&gt;The other part is planning. Essentially, you need to identify groups of features, and then answer two big questions for each feature set. One, what's the business value here? &lt;em&gt;Or, how do we get paid?&lt;/em&gt; Two, how is this thing going to work?&lt;br /&gt;&lt;br /&gt;I'm not sure I've not forgotten about something completely hugemongous.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-1044716719146912949?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/1044716719146912949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=1044716719146912949' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1044716719146912949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1044716719146912949'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/12/documented.html' title='Documented?'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-3861428552697473625</id><published>2008-12-13T00:40:00.000-08:00</published><updated>2008-12-13T08:06:38.149-08:00</updated><title type='text'>Women Are Out of The IT Compartment</title><content type='html'>Why do we create stupid compartments for everything?  At first, I thought it was just Americans with their shared cultural stupidity:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You can be black, or white. Or maybe brown or yellow, but those aren't really used. But I've actually checked &lt;span class="Apple-style-span" style="font-style: italic;"&gt;White&lt;/span&gt; on forms in school.&lt;/li&gt;&lt;li&gt; &lt;em&gt;Red state&lt;/em&gt; means religious conservative Republican, &lt;em&gt;blue state&lt;/em&gt; means agnostic liberal Democrat.&lt;/li&gt;&lt;/ul&gt;But you know what? When it comes to programming, the whole world shares another category:&lt;ul&gt;&lt;li&gt; Programmer - a guy who likes tech. Pretty geeky, usually.&lt;/li&gt;&lt;/ul&gt;It's a shame, really. And it's everyone's fault. &lt;a href="http://blogs.tedneward.com/2008/12/12/Ruminations+On+Women+In+IT.aspx"&gt;Another blog&lt;/a&gt; flipped a switch about the little ways we help permeate the &lt;em&gt;chicks don't do IT&lt;/em&gt; thing. In the article, it was shopping, where a salesman kept ansering the brother, instead of the sister who asked the questions.&lt;blockquote&gt;The entire time we were in the store, despite the fact that it was my sister asking the questions, despite the fact that I only answered questions that she asked of me directly (in other words, I was there to help her, not to help the sales guy sell to her), almost the entire conversation was spent with the sales guy talking to me, even if he was answering her question. His body language was unquestionably that of, "She's clearly not capable of making this decision herself", and addressed everything to me, despite her repeated attempts to catch his eye and have him talk to her, the actual purchaser with the question.&lt;br /&gt;&lt;/blockquote&gt;My concern is that this is a kind of "shared cultural discrimination". You stick a sales&lt;em&gt;woman&lt;/em&gt; in there answering questions, and most of the time, I think you'd get the same result. I don't think this is a "boy's club" sort of situation, but a general acceptance that women just aren't interested and don't really know about this &lt;em&gt;sort of stuff&lt;/em&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-3861428552697473625?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/3861428552697473625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=3861428552697473625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/3861428552697473625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/3861428552697473625'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/12/women-are-out-of-it-compartment.html' title='Women Are Out of The IT Compartment'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8769103021802252325</id><published>2008-09-24T13:39:00.000-07:00</published><updated>2008-09-24T13:47:56.770-07:00</updated><title type='text'>Build some scala-swing documentation for 0.1</title><content type='html'>&lt;em&gt;Beta? I don't need no stinkin' beta!&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;One thing about HTML documentation, I love them links. Especially the ability to navigate up and down the inheritence heirarchy. Mmm... hierarchy.&lt;br /&gt;&lt;br /&gt;But the new &lt;code&gt;scala-swing&lt;/code&gt; project, which looks interesting, and is included in the latest release candidate for scala, has no documentation. Ergo, I went about tweaking the &lt;code&gt;src&lt;/code&gt; jar just to get things a goin.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;First&lt;/b&gt;, I unjarred the &lt;code&gt;scala-swing-src.jar&lt;/code&gt; file into the directory &lt;code&gt;scala-swing-src&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Second&lt;/b&gt;, I changed the &lt;code&gt;scala.home&lt;/code&gt; property to my scala distribution, which I had downloaded.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Third&lt;/b&gt; I added this code to the &lt;code&gt;build.xml&lt;/code&gt; file.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;      &amp;lt;taskdef name=&amp;quot;scaladoc&amp;quot;&lt;br /&gt;        classname=&amp;quot;scala.tools.ant.Scaladoc&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;classpath&amp;gt;&lt;br /&gt;          &amp;lt;pathelement location=&amp;quot;${scala.home}/lib/scala-compiler.jar&amp;quot;/&amp;gt;&lt;br /&gt;          &amp;lt;pathelement location=&amp;quot;${scala.home}/lib/scala-library.jar&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/classpath&amp;gt;&lt;br /&gt;      &amp;lt;/taskdef&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;property name=&amp;quot;docs.dir&amp;quot; value=&amp;quot;api&amp;quot; /&amp;gt;&lt;br /&gt;      &amp;lt;property name=&amp;quot;sources.dir&amp;quot; value=&amp;quot;..&amp;quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;target name=&amp;quot;docs&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;mkdir dir=&amp;quot;${docs.dir}&amp;quot;  /&amp;gt;&lt;br /&gt;        &amp;lt;scaladoc&lt;br /&gt;          srcdir=&amp;quot;${sources.dir}&amp;quot;&lt;br /&gt;          destdir=&amp;quot;${docs.dir}&amp;quot;&lt;br /&gt;          deprecation=&amp;quot;yes&amp;quot; unchecked=&amp;quot;yes&amp;quot;&amp;gt;&lt;br /&gt;          &amp;lt;classpath&amp;gt;&lt;br /&gt;            &amp;lt;pathelement location=&amp;quot;${scala.home}/lib/scala-compiler.jar&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;pathelement location=&amp;quot;${scala.home}/lib/scala-library.jar&amp;quot;/&amp;gt;&lt;br /&gt;          &amp;lt;/classpath&amp;gt;&lt;br /&gt;            &amp;lt;include name=&amp;quot;**/*.scala&amp;quot;  /&amp;gt;&lt;br /&gt;        &amp;lt;/scaladoc&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Third&lt;/b&gt;, I ran the command &lt;code&gt;ant docs&lt;/code&gt; from the &lt;code&gt;scala-swing-src/doc&lt;/code&gt; directory, and blammo, I've got scaladoc API.&lt;br /&gt;&lt;br /&gt;Note: this is not a true step-by-step howto, because it's getting late. You will have to engage your brain to fill in 1-2 blank spots in the steps above.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8769103021802252325?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8769103021802252325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8769103021802252325' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8769103021802252325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8769103021802252325'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/09/build-some-scala-swing-documentation.html' title='Build some scala-swing documentation for 0.1'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-1500928707191640864</id><published>2008-03-17T15:29:00.000-07:00</published><updated>2008-03-17T15:44:09.999-07:00</updated><title type='text'>tickets != tasks</title><content type='html'>My company uses &lt;a href="http://trac.edgewall.org/"&gt;trac&lt;/a&gt;, which so far, is my favorite project management tool that I've used.  It gets out of the way for the most part. Really it is perfect for a group of code monkeys. But it is missing something very important - scheduling.&lt;br /&gt;&lt;br /&gt;The basic unit of Trac are tickets, assigned to milestones. This works well when it comes to reporting problems, but also pretty nice when you need to see the issues you are solving. That's the core of a ticketing system. Issues happen, you write a ticket. A series of issues tracked by milestone do a good job of saying &lt;em&gt;this is what happened, what will happen, what we want&lt;/em&gt;. It does not, however, say &lt;em&gt;when&lt;/em&gt; anything will happen.&lt;br /&gt;&lt;br /&gt;To say when, it's better to start with an issue, and then try to define small steps that get you going. This seems like a natural one-to-many ratio of issues to tasks, and that's true. But the real problem lies with the fact you rarely can take the one issue and accurately define the many tasks. As time flies, your many will grow, and change, as you learn things, specs change, whatever. Ultimately it is very hard to embed task analysis inside of an issue-focused system. &lt;br /&gt;&lt;br /&gt;While tasks start their lives once someone is taking on an issue, the culmination of tasks needs to be seen in the perspective of time. In fact, I would also like to see things like milestones in such a calendar as well. A project calendar like this would spot the &lt;em&gt;when&lt;/em&gt; of a project. And leave the tickets and milestones to simply define the &lt;em&gt;what happened&lt;/em&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-1500928707191640864?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/1500928707191640864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=1500928707191640864' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1500928707191640864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1500928707191640864'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/tickets-tasks.html' title='tickets != tasks'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-6022351348403790742</id><published>2008-03-15T05:46:00.000-07:00</published><updated>2008-03-15T05:53:44.680-07:00</updated><title type='text'>Time to market isn't everything</title><content type='html'>Tim Bray brings up an &lt;a href="http://www.tbray.org/ongoing/When/200x/2008/03/13/Time-To-Market"&gt;argument&lt;/a&gt; I have heard too often - time to market kills your competition. I think it is important, yes, but it does not kill competition. There are too many important examples of time to market &lt;strong&gt;not&lt;/strong&gt; killing your competition.&lt;br /&gt;&lt;br /&gt;Let's look at some of the most important high-tech goodies out there. Internet Explorer for web browsers, Google in search, iPods in music players. All of these did not exist when competition "had the market". They came in late and took the market by being better in different ways. IE was easier to set up in the OS - you might say illegally easy to set up, but there you go. Google was simply easier to find what you really wanted. iPods were all around superior devices to any other mp3 player I used - much easier to navigate.&lt;br /&gt;&lt;br /&gt;But the key aspect here is that these players were &lt;strong&gt;different&lt;/strong&gt; and &lt;strong&gt;significantly better&lt;/strong&gt; than their competition. If you have the market, and you capitalize on that by learning and adjusting quickly to what people need, it's yours. But if you stop innovating, it's just a matter of time before folks flock to something else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-6022351348403790742?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/6022351348403790742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=6022351348403790742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6022351348403790742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6022351348403790742'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/time-to-market-isnt-everything.html' title='Time to market isn&apos;t everything'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-5113280414664014060</id><published>2008-03-14T10:07:00.001-07:00</published><updated>2008-03-14T10:16:04.140-07:00</updated><title type='text'>Monkeys don't make good admins</title><content type='html'>Code monkeys are pretty damn amazing at some complex tasks, like climbing trees, or peeling bananas. We do not, however, like computer recipes. An example computer recipe: &lt;em&gt;to upgrade Foo, check script X on machine B and then restart application Y on machine C&lt;/em&gt;. We don't become programmers to &lt;strong&gt;follow&lt;/strong&gt; recipes, we want to &lt;strong&gt;make&lt;/strong&gt; them and have the computer do them for us.&lt;br /&gt;&lt;br /&gt;You better be careful if you make your code monkeys follow recipes. Many of them, like me, get irritated. Enough irritation, and we get angry. When we get angry, we start throwing poo. And then you just end up with shit everywhere.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-5113280414664014060?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/5113280414664014060/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=5113280414664014060' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5113280414664014060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5113280414664014060'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/monkeys-dont-make-good-admins.html' title='Monkeys don&apos;t make good admins'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8024016475681534416</id><published>2008-03-13T07:01:00.000-07:00</published><updated>2008-03-13T18:53:56.440-07:00</updated><title type='text'>What? No delete?</title><content type='html'>Today I was shocked to learn I could not delete a simple web page connected to my domain on Yahoo's domain registration site. &lt;br /&gt;&lt;br /&gt;Recently, I moved my blog from a shared hosting site to blogger. I had been using it to learn some web design, and now I've decided it was more important to spend the extra time learning how to write. Leave the graphic stuff behind for now, and just point the ol' domain at blogger, which is easy to do.&lt;br /&gt;&lt;br /&gt;Except the yahoo &lt;code&gt;CNAME&lt;/code&gt; forwarding will forward &lt;code&gt;www.tristanhunt.com&lt;/code&gt; but not &lt;code&gt;tristanhunt.com&lt;/code&gt;. Which is lame, because I don't like &lt;code&gt;www.&lt;/code&gt; because it makes no sense.&lt;br /&gt;&lt;br /&gt;While experimenting with just having a simple page do a manual redirect as a kludge, I learned two things about the Yahoo system:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;They do not allow you to edit your simple landing page for your domain&lt;/li&gt;&lt;li&gt;Once generated they &lt;em&gt;do not allow you to delete it&lt;/em&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;a href="http://help.yahoo.com/l/us/yahoo/smallbusiness/domains/domainfeatures/starterwebpage/starterwebpage-22.html"&gt;Check for yourself&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm sorry, but I do not care who you are. You can make people jump hoops to delete things that might mess up functionality. But not allowing deletion? I'm sorry, this is just stupid.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8024016475681534416?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8024016475681534416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8024016475681534416' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8024016475681534416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8024016475681534416'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/what-no-delete.html' title='What? No delete?'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8995922700838871501</id><published>2008-03-12T10:25:00.000-07:00</published><updated>2008-03-12T10:41:56.538-07:00</updated><title type='text'>Java one-beaners</title><content type='html'>&lt;p&gt;The get and set Java conventions sure seem out of date. They are the coding equivalent of pegging your Jeans. They repeat rather a rather boring statement, and feel like a Dick and Jane book. "See class. Class has Thing. Set Thing. Get Thing. Good Thing!"&lt;br /&gt;&lt;/p&gt;To put this drudgery in its place, I don't give the dumb &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; methods the same posh multi-line status I give other methods. Instead, I relegate them to one-liners, which I henceforth dub "the one-beaner". When the one-beaner needs explaining, I stick the first explaination on the get method.&lt;br /&gt;&lt;pre&gt;private Thing thing;&lt;br /&gt;&lt;br /&gt;/** You really want to play with this class' thing. */&lt;br /&gt;public Thing getThing() { return thing; }&lt;br /&gt;public void setThing(Thing thing) { this.thing = thing; }&lt;br /&gt;&lt;/pre&gt;If my one-beaner needs extra logic, then it gets the full treatment. Extra whitespace like newlines to breathe. A comment describing perhaps why. Perhaps a shiatsu.&lt;br /&gt;&lt;pre&gt;private Thing thing;&lt;br /&gt;&lt;br /&gt;/** This thing ain't null. */&lt;br /&gt;public Thing getThing() { return thing; }&lt;br /&gt;&lt;br /&gt;/** Slap a IllegalArgumentException at the fool who tries to nullify this thing. */&lt;br /&gt;public void setThing(Thing thing)&lt;br /&gt;{&lt;br /&gt; if (thing == null) throw new IllegalArgumentException("thing != null, fool!");&lt;br /&gt; this.thing = thing;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now, when I read my code, I can tell the one-beaners from the code that actually does something.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8995922700838871501?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8995922700838871501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8995922700838871501' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8995922700838871501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8995922700838871501'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/java-one-beaners.html' title='Java one-beaners'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-6137515779005611572</id><published>2008-03-12T03:06:00.002-07:00</published><updated>2008-03-12T03:15:08.526-07:00</updated><title type='text'>Work hard like Microsoft, and people will still say you suck</title><content type='html'>&lt;p&gt;Has anyone in your company ever said "let's add a Word converter for this". And then you added a word converter, probably from an open source project. And then from that point people sent end endless variations of files that broke or otherwise had a lot of problems.&lt;/p&gt; &lt;p&gt;It's not your fault. And Microsoft is not out to get you. &lt;/p&gt; &lt;p&gt;Joel Sposky had an &lt;a href="http://www.joelonsoftware.com/items/2008/02/19.html" mce_href="http://www.joelonsoftware.com/items/2008/02/19.html"&gt;interesting and informative article&lt;/a&gt; about the details of the Office File formats. My 5 second summary — the formats were designed for speed back when it was important. And Microsoft has since incrementally developed rather than redesign. And now, the best way to convert an Office document is to run office. &lt;/p&gt; &lt;p&gt;You can automate Office pretty easily - in Windows, at least. But then you need: &lt;/p&gt;&lt;ol&gt;&lt;li&gt;Office&lt;/li&gt;&lt;li&gt;Windows&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Hard? No. However, if you happen to be a Linux shop, and we are, the idea of having a lone Windows server in your network feels just like throwing salt in your eyes or jumping onto a bed of nails repeatedly. For many whiny trivial reasons. And one big good reason.&lt;/p&gt; &lt;p&gt;You have to administer a new operating system. Yes, Windows sounds easy, but it's not. Probably easier than Linux to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;learn&lt;/span&gt; yes, but it's a ton of complexity to throw into a system where your typical hires might only use a Windows box for playing a few games.Thus, dealing Microsoft Office forces you into a really high-cost of training, and doing something very complicated (creating a heterogeneous network), just to &lt;span class="Apple-style-span" style="font-style: italic;"&gt;convert content&lt;/span&gt;. For a non-Microsoft shop, this is the sort of trivial-turned-oh-my-god experience that makes you say "Microsoft Sucks". &lt;/p&gt; &lt;p&gt;What's interesting to me here is that Microsoft doesn't suck. They took on a nasty problem at the beginning, to make interactivity fast despite the hardware. They solved it, and then kept that part around, extending the software when the original problem went away — hardware got faster. &lt;/p&gt; &lt;p&gt;There is a lesson here. Pay attention to the nasty problems you solve today. Adding complexity  to handle those nasty problems could well end up with the Office experience. Work very hard, only to have people say, "You suck".&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-6137515779005611572?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/6137515779005611572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=6137515779005611572' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6137515779005611572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6137515779005611572'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/work-hard-like-microsoft-and-people.html' title='Work hard like Microsoft, and people will still say you suck'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-1285310752410347093</id><published>2008-03-12T03:06:00.001-07:00</published><updated>2008-03-12T03:15:02.230-07:00</updated><title type='text'>Get that stupid JList to stop wrapping</title><content type='html'>&lt;p&gt;Here's the deal. I've got a list of "recipes", each is a node in a &lt;code&gt;javax.swing.JList&lt;/code&gt;. Each recipe is a panel of many things: image, title, etc. The JList didn't do too much until I figured out this:&lt;/p&gt; &lt;pre&gt;recipesList.setVisibleRowCount(1);&lt;/pre&gt; &lt;p&gt;A-ha, now the list no longer automatically wraps when the surrounding window (which is a &lt;code&gt;JScrollPane&lt;/code&gt;, of course.) Maybe I'm just hung over, but this wasn't obvious to me.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-1285310752410347093?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/1285310752410347093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=1285310752410347093' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1285310752410347093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1285310752410347093'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/get-that-stupid-jlist-to-stop-wrapping.html' title='Get that stupid JList to stop wrapping'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-7865629880355054521</id><published>2008-03-12T03:05:00.003-07:00</published><updated>2008-03-12T03:14:57.461-07:00</updated><title type='text'>The NetBeans workaround effect</title><content type='html'>&lt;p&gt;NetBeans continues to toy with my fantasies. It is simpler than Eclipse, and far more humane. There are no Eclipse quirks, like having to reconfigure your environment whenever you want a different workspace. And the visual editor, which builds off of the &lt;a href="https://appframework.dev.java.net/" mce_href="https://appframework.dev.java.net/" title="Swing Application Framework"&gt;Swing Application Framework&lt;/a&gt;, allows you to do slam together a few components together to try things out.&lt;/p&gt; &lt;p&gt;But the quality! Ugh! There were 2-3 moments today alone where the editor simply froze when doing editing. I am not sure who is making the QA decisions for Sun, but these kinds of experiences leave me asking &lt;span style="font-style: italic;" class="Apple-style-span"&gt;do they care?&lt;/span&gt; Which is sad, because I'm sure they do — but someone won the argument to ship it.&lt;/p&gt; &lt;p&gt;In the end, NetBeans is now my Prototyper. I use it to create prototypes, particularly for GUIs. Once I get familiar with what I want, however, everything gets transferred to Eclipse.&lt;/p&gt; &lt;p&gt;What I find interesting is that I haven't really bitched about this, other than this blog.&lt;/p&gt; &lt;p&gt;There is a lesson here for other products. If people start having problems with your products, they will probably start finding a workaround. And not tell you.&lt;/p&gt; &lt;p&gt;For Java editors, where you have the Eclipse beast — lots of crap, but free, and generally works — I seriously doubt there's a market for competition. And so we are left with this: feel like you are overpaying for a product, or getting used to workarounds.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-7865629880355054521?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/7865629880355054521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=7865629880355054521' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/7865629880355054521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/7865629880355054521'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/netbeans-workaround-effect.html' title='The NetBeans workaround effect'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-3692802358638159670</id><published>2008-03-12T03:05:00.001-07:00</published><updated>2008-03-12T03:14:52.088-07:00</updated><title type='text'>The programmer's portfolio</title><content type='html'>&lt;p&gt; OK, my company is starting to hire people. What is one of the first things&lt;br /&gt; the boss says? "It's so hard to find J2EE people." And then we talk a bit.&lt;br /&gt; Sure enough, the job advertisement is rife full of toolkits. You've seen&lt;br /&gt; them, these job announcements that look like a book an&lt;br /&gt; O'Reilly book catalog listing? Does anyone think that sounds like a&lt;br /&gt; fun company to work for? &lt;/p&gt; &lt;p&gt; I want to see portfolios. If programmers put together highlights of their&lt;br /&gt; work, displaying what they are most proud of in technical detail, I'd&lt;br /&gt; probably hire them in a second. Especially if they could write well. &lt;/p&gt; &lt;p&gt; Now the interesting question becomes, what is a programmer's portfolio?&lt;br /&gt; A beautiful leather-bound article on your favorite API. A website displaying&lt;br /&gt; your interface development work. Articles on decisions you've made. &lt;/p&gt; &lt;p&gt; Something tells me the creation of a portfolio would be a better learning&lt;br /&gt; experience in and of itself. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-3692802358638159670?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/3692802358638159670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=3692802358638159670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/3692802358638159670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/3692802358638159670'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/programmers-portfolio.html' title='The programmer&apos;s portfolio'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-6852431564827187036</id><published>2008-03-12T03:04:00.001-07:00</published><updated>2008-03-12T03:14:47.347-07:00</updated><title type='text'>Interesting unit tests in EJB code are hard to find</title><content type='html'>&lt;p&gt;You write Java. You are using your wonderful coverage tools like &lt;a href="http://emma.sourceforge.net/" mce_href="http://emma.sourceforge.net/" title="emma code coverage tool"&gt;EMMA&lt;/a&gt; to make sure your unit tests are great. You know what you can spend a lot of time keeping track of? Methods like this.&lt;/p&gt; &lt;pre&gt;public int getFoo()&lt;br /&gt;{&lt;br /&gt;  return foo;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setFoo(int foo)&lt;br /&gt;{&lt;br /&gt;  this.foo = foo;&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;So f-in what? You write tests that check code that Eclipse will &lt;i&gt;generate a warning for&lt;/i&gt;. I guess this is just a statement that coverage isn't everything. And I am &lt;a href="http://stuffthathappens.com/blog/2007/11/26/the-problem-with-emma/" mce_href="http://stuffthathappens.com/blog/2007/11/26/the-problem-with-emma/"&gt;not the first&lt;/a&gt;. But I would go further, and say that coverage is really, really misleading. Particularly when you are writing server-side Java code.&lt;/p&gt; &lt;h2&gt;Not so obvious, but still meaningless, unit testing&lt;/h2&gt; &lt;p&gt;EJB code is notoriously hard to test. Even these days, in the era of &lt;a href="http://easymock.org/" mce_href="http://easymock.org/"&gt;automatic object mocking&lt;/a&gt;. Take a look at the kind of bean code I see a lot of these days.&lt;/p&gt; &lt;pre&gt;public void addFooToBar(long barId, Foo foo) throws SomeStuff&lt;br /&gt;{&lt;br /&gt;  // em is an EntityManager instance&lt;br /&gt;  Bar bar = (Bar)em.createQuery("from Bar where id=:barId and other='constraint'")&lt;br /&gt;      .setParameter("barId", barId)&lt;br /&gt;      .getSingleResult();&lt;br /&gt;&lt;br /&gt;  bar.addFoo(foo);&lt;br /&gt;&lt;br /&gt;  em.merge(bar);&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;Initially, I was like, wow, with stuff like &lt;a href="http://easymock.org/" mce_href="http://easymock.org/"&gt;EasyMock&lt;/a&gt; I can actually get this covered! But then you realize that all interaction with the &lt;code&gt;EntityManager&lt;/code&gt; is meaningless when you mock things out. What, are you going to do a &lt;i&gt;string comparison&lt;/i&gt; that the query will work? Are you really interested in the call to &lt;code&gt;.addFoo&lt;/code&gt;?&lt;/p&gt; &lt;p&gt;This kind of code is just as meaningless in a Java-based unit test as the standard bean property accessors. The reason of course is that all the real logic is in the string data — the database query — and interaction with the database.&lt;/p&gt; &lt;h2&gt;Two interesting problems about uninteresting code&lt;/h2&gt; &lt;p&gt;First, I want a way to measure interesting code. Number of statements? No, not really. If you could figure out anything that really interacts with certain interfaces and classes, like the &lt;code&gt;EntityManager&lt;/code&gt;, well, maybe that might be interesting.&lt;/p&gt; &lt;p&gt;Second, I want a different framework that acts like a unit test for database interaction, both with toolkits like &lt;a href="http://hibernate.org/" mce_href="http://hibernate.org"&gt;Hibernate&lt;/a&gt;, as well as straight JDBC. This, I think I could come up with. But I wouldn't use a Java-based unit test framework. That tests Java code, and here, I want to test database interaction. Different problems, and how well you write a DB test depends upon how far you push your schema, not on how many lines of Java code got executed. So the second problem is, with a DB-oriented test framework, how do you measure your schema variations?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-6852431564827187036?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/6852431564827187036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=6852431564827187036' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6852431564827187036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/6852431564827187036'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/interesting-unit-tests-in-ejb-code-are.html' title='Interesting unit tests in EJB code are hard to find'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-7569316734965957091</id><published>2008-03-12T03:03:00.002-07:00</published><updated>2008-03-12T03:14:41.239-07:00</updated><title type='text'>Why spam wins</title><content type='html'>&lt;p&gt;Spammers have the best QA job in the universe: unlimited variations until you hit a win. It's not about being perfect, it's about not being blocked. And it's not like you need fantastic results. Just look at the latest spam that beat GMail today. And GMail is probably the best spam-blocker there is. &lt;/p&gt; &lt;pre&gt;&lt;br /&gt;www SuggestionDeal com It is [Unknown Tag *$$thetime* Please Fix] [Unknown Tag *$$rest* Please Fix]&lt;br /&gt;Just [Unknown Tag *$$come* Please Fix] And [Unknown Tag *$$most* Please Fix] [Unknown Tag *$$girl* Please Fix] can [Unknown Tag *$$give* Please Fix]&lt;br /&gt;When you see [Unknown Tag *$$magic* Please Fix] Show Without paying any penny&lt;br /&gt;&lt;br /&gt;marveling ophthalmia namability&lt;br /&gt;oilway folklore kebabs ephyra amaretto&lt;br /&gt;vitellose expressively alkalizate synodontoid juniorship&lt;br /&gt;bopyridian horsedom fervor drips&lt;br /&gt;&lt;/pre&gt; &lt;p&gt;Yes, there's nothing to really click on, and it really is mostly nonsense, notice how the spammer just has to hit a &lt;i&gt;suggestion&lt;/i&gt; of a real link. Otherwise, it's just nonsense. There's something beautiful in this. I think it's the fact that they are using machines for brute force duplication, but, the strength is still in our power to bridge gaps automatically.&lt;/p&gt; &lt;p&gt;To me, there is a design lesson here: let the machine do what it does best, and let people bridge the right gaps. Things will happen — you don't always have to force people to &lt;i&gt;see everything&lt;/i&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-7569316734965957091?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/7569316734965957091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=7569316734965957091' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/7569316734965957091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/7569316734965957091'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/why-spam-wins.html' title='Why spam wins'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-4659965665487804017</id><published>2008-03-12T03:03:00.001-07:00</published><updated>2008-03-12T03:14:36.396-07:00</updated><title type='text'>Only your unit needs this unit test</title><content type='html'>&lt;p&gt;Unit testing is great – test your &lt;i&gt;code&lt;/i&gt;, but &lt;i&gt;without&lt;/i&gt; your system!&lt;/p&gt; &lt;p&gt;It shows it's promise when you're out a fix to a problem, and that problem happens to be buried. So buried that reproduction only happens when the stars align and your operating system has a totally different setup and time zone or your application server decides it's going to leak or something equally nefarious.&lt;/p&gt; &lt;p&gt;The real difficulty is being able to &lt;b&gt;quickly&lt;/b&gt; send wacky input to your code to test it, no matter where it runs. Because you can't always easily control these inputs.&lt;/p&gt; &lt;p&gt;And the first mistake is that people start building up a test for the unit &lt;i&gt;and everything it references&lt;/i&gt;. This is bad, because then you're not just testing your unit, your testing the entire subsystem. Which often has it's own runtime requirements.&lt;/p&gt; &lt;p&gt;This leads me to my rule...&lt;/p&gt; &lt;h3&gt;Only your unit needs your unit test&lt;/h3&gt; &lt;p&gt;When you need to run everything underneath the unit, what happens if the unit happens to be at the top of the pile? Yep, you end up running the entire damn thing. And chances are, that isn't easy.&lt;/p&gt; &lt;p&gt;Plus...&lt;/p&gt; &lt;h3&gt;Java has made this easy for you&lt;/h3&gt; &lt;p&gt;When your code references other code through interfaces, you have an automatic win. Your unit test just has to somehow mimic how the interface is attached to reality. And lo and behold, there are alot of ways we achieve &lt;a href="http://martinfowler.com/articles/injection.html" mce_href="http://martinfowler.com/articles/injection.html"&gt;dependency injection&lt;/a&gt; in Java. &lt;a href="http://springframework.org/" mce_href="http://springframework.org"&gt;Spring&lt;/a&gt;, &lt;a href="http://code.google.com/p/google-guice/" mce_href="http://code.google.com/p/google-guice/"&gt;Guice&lt;/a&gt;, EJBs.&lt;/p&gt; &lt;p&gt;We use a lot of EJBs, so I'm going to mention how that can work.&lt;/p&gt; &lt;h3&gt;Cheat on your EJBs&lt;/h3&gt; &lt;p&gt;Generally, our system uses a lot of stateless session beans, so we actually just use the whole injection framework purely for connecting up code. So all you need to do is fake the injection.&lt;/p&gt; &lt;pre&gt;&lt;br /&gt;public static void inject(Object target, String fieldName, Object value) throws ...&lt;br /&gt;{&lt;br /&gt;  Field field = target.getClass().getDeclaredField(fieldName);&lt;br /&gt;  field.setAccessible(true);&lt;br /&gt;  field.set(target, value);&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;Yeah, it seems dirty, but that works on each instance I've actually needed, because we don't happen to have much else in the way of dependencies.&lt;/p&gt; &lt;p&gt;So now, to say, test my EJB, I might write the following code:&lt;/p&gt; &lt;pre&gt;&lt;br /&gt;MyEJB myEJB = new MyEJB();&lt;br /&gt;MyDependency myDependency = new TestDependency(); // TestDependency derives from the MyDependency interface&lt;br /&gt;inject(myEJB, "myDependency", myDependency);&lt;br /&gt;&lt;br /&gt;// do test stuff&lt;/pre&gt; &lt;p&gt;Here is what MyEJB probably looks like:&lt;/p&gt; &lt;pre&gt;&lt;br /&gt;@Stateless&lt;br /&gt;public class MyEJB implements Whatever&lt;br /&gt;{&lt;br /&gt; @EJB private MyDependency myDependency;&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;Of course, if you have more initialization and whatever going on, then you might want to take a look at &lt;a href="https://easygloss.dev.java.net/" mce_href="https://easygloss.dev.java.net/"&gt;EasyGloss&lt;/a&gt; for your needs.&lt;/p&gt; &lt;p&gt;Another issue is that &lt;code&gt;TestDependency&lt;/code&gt; really doesn't need to do much, other than return some stub stuff. This leads to...&lt;/p&gt; &lt;h3&gt;Use EasyMock to fake it&lt;/h3&gt; &lt;p&gt;The EasyMock project was born to handle generating things like &lt;code&gt;TestDependency&lt;/code&gt;. You have a lot of control on what you want to guarantee happens to things like &lt;code&gt;MyDependency&lt;/code&gt; when &lt;code&gt;MyEJB&lt;/code&gt; is running. The above example becomes&lt;/p&gt; &lt;pre&gt;&lt;br /&gt;MyEJB myEJB = new MyEJB();&lt;br /&gt;MyDependency myDependency = createMock(MyDependency.class);&lt;br /&gt;inject(myEJB, "myDependency", myDependency);&lt;br /&gt;&lt;br /&gt;expect(myDependency.callMe(5678309))&lt;br /&gt;  .andReturn("No thanks, I'm doing my hair");&lt;br /&gt;&lt;br /&gt;replay(myDependency);&lt;br /&gt;// do test stuff&lt;br /&gt;verify(myDependency);&lt;/pre&gt; &lt;p&gt;Now we have verified that &lt;code&gt;MyDependency&lt;/code&gt; successfully declined &lt;code&gt;MyEJB&lt;/code&gt;'s advances.&lt;/p&gt; &lt;h3&gt;Mocking your code is great, but...&lt;/h3&gt; &lt;p&gt;A final note is pointing out the major weaknesses of this approach. First, because real system isn't testing, mocking guarantees consistency, not success. Second, if your code isn't described in wonderful Java, you are probably not going to test it using this approach.&lt;/p&gt; &lt;p&gt;And what is used in Java but isn't Java? Database queries, of course... ew...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-4659965665487804017?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/4659965665487804017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=4659965665487804017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/4659965665487804017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/4659965665487804017'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/only-your-unit-needs-this-unit-test.html' title='Only your unit needs this unit test'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8797649767242902025</id><published>2008-03-12T03:02:00.002-07:00</published><updated>2008-03-12T03:14:30.962-07:00</updated><title type='text'>Project management shouldn't involve crystal balls</title><content type='html'>&lt;p&gt;One of the hardest things to do is to predict the future. The best practice is not to do it. But, sure enough, software companies often become fortune tellers for their customers. They need to tell them that if the customer gives us this money today, they will receive great fortune in only 12 calendar weeks!&lt;/p&gt; &lt;p&gt;So, it's time to make a plan. And a plan begins by writing a specification. Often called a spec – but only because the actual thought put into the document amounts to a speck of detail. Anyhow, a plan arrives at a promise to fulfill. Great people actually fulfill their promises. Most people miss a few things unintentionally. But no matter how well we fulfill our promises, we take the specification and turn it into an estimate of hours, as if we were perfect machines that input a task and output productivity, 8 perfect hours a day.&lt;/p&gt; &lt;p&gt;Or, a perfect 7.5 hours in Austria, which really means 8 hours a day except for 6.5 hours on Friday.&lt;/p&gt; &lt;p&gt;So, most companies assign such plans to a group of people to pull off. The first analysis tends to be something like this: our estimate says &lt;code&gt;n&lt;/code&gt; hours, we have &lt;code&gt;m&lt;/code&gt; people, and they tend to work at &lt;code&gt;l&lt;/code&gt; hours per week. So, &lt;code&gt;(n ÷ l) ÷ m&lt;/code&gt; calendar weeks!&lt;/p&gt; &lt;p&gt;I have never seen such a system work where you have multiple people, or for those math-inclined, &lt;code&gt;m &gt; 1&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;So the next step turns into a process I like to call &lt;i&gt;task deification&lt;/i&gt;. People turn their project into a series of interconnected tasks. One leads into another. And all roads lead towards a Microsoft Project Gantt chart, or some other surrogate. The reason behind all of this work is that you can easily visualize a way to parallelize your tasks.&lt;/p&gt; &lt;p&gt;Task deification isn't a horrible idea, it just doesn't work. The first mistake is assuming your task breakdown is unambiguous, and everyone will do it at about the same pace. If you have done this sort of thing before, it becomes easier, because a group of people will eventually all learn the same things. We work in software, however, and software has a underlying feature that the cost of manufacturing multiple units is practically nothing. So why would you ever solve the same problems twice? Sure, it happens, but chances are, the more you do of the same thing in software, the less significant that thing is.&lt;/p&gt; &lt;p&gt;The other reason task deification sucks is that it inverts the focus to tasks from where it should be – the people doing the work. On the &lt;a href="http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=000076&amp;amp;topic_id=1&amp;amp;topic=Ask+E%2eT%2e" mce_href="http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=000076&amp;amp;topic_id=1&amp;amp;topic=Ask+E%2eT%2e" title="Discussion forum on project management on Edward Tufte website"&gt;website of Edward Tufte&lt;/a&gt; I found a wonderful approach to changing your overall scheduling view. Basically, you exchange axes of the Gantt chart from tasks over time to people over time. I like this approach, because you think first about the people.&lt;/p&gt; &lt;p&gt;People do strange things, especially in software. One programmer might need quiet time, away from disruption, to read about a platform they haven't used before. One QA tester may only understand your product as a black box, and not understand every place it can be broken. One programmer might develop higher-quality code, and another might need someone else to write the documentation. What you need to see are these people and how they operate together. Your tasks will then get great detail when written from the perspective of the person that must do them, instead of the vague specification written by one person.&lt;/p&gt; &lt;p&gt;If the person writing the specification is the person doing all the work, you will have very few problems. Otherwise, you need to step back and look at everything from your people. Projects are made by people. Multiple people do not become one machine. A team is just an organization of people, typically unorganized.&lt;/p&gt; &lt;h3&gt;Towards better software projects&lt;/h3&gt; &lt;p&gt;Over time, I've discovered a few principles to planning software that actually work.&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Design until you can break the problem into chunks, each that will be less than 16 hours of work – the infamous "couple of days".&lt;/li&gt;&lt;li&gt;You only need to be definite about the next month or two, but you need to think, and design, to the whole.&lt;/li&gt;&lt;li&gt;Any other person that might do the work better understand all of the details of this chunk.&lt;/li&gt;&lt;li&gt;Plan to adjust; but don't let that be an excuse to ignore details.&lt;/li&gt;&lt;li&gt;The person that does the work should define the task, or at least complete it.&lt;/li&gt;&lt;li&gt;&lt;i&gt;A volunteered idea is always better than an assigned one.&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;The thing most companies completely mess up is getting their workers to volunteer. They put themselves in a situation where they have to plan everything up front. And thus, their employees lose all the potential energy and excitement of seeing their one of their ideas come to fruition.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8797649767242902025?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8797649767242902025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8797649767242902025' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8797649767242902025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8797649767242902025'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/project-management-shouldnt-involve.html' title='Project management shouldn&apos;t involve crystal balls'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-5943973818588894123</id><published>2008-03-12T03:02:00.001-07:00</published><updated>2008-03-12T03:14:23.730-07:00</updated><title type='text'>In Struts, one page means one action</title><content type='html'>&lt;p&gt;In our application, we have this one page where people can edit content. This page has links to edit other aspects as well. When you navigate to another page, we save the content before navigating. It makes sense – forcing people to save just to edit another aspect of the content sucks.&lt;/p&gt; &lt;p&gt;Where this comes tricky is error handling. We use &lt;a href="http://struts.apache.org/" mce_href="http://struts.apache.org/" title="Struts"&gt;Struts&lt;/a&gt;. And for this editing page, we have several simple Actions that back all this interaction. One action to save, one to load the content editing page, others to handle the other operations. We connect these actions through redirects, which seems like a freaking bad idea to me. Because when an error happens, you can no longer redirect, because we want to add all kinds of error messages to response object. And if you redirect, poof, no more errors!&lt;/p&gt; &lt;p&gt;So I have a rule, each page is backed by a single action. Any operation rolls through that action. This allows you to simply call one of the other functions rather than configure a redirect mechanism. Your API is cleaner, the logic flow can be read in a single file, and hey, if you have errors, you can report them to the same location!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-5943973818588894123?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/5943973818588894123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=5943973818588894123' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5943973818588894123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/5943973818588894123'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/in-struts-one-page-means-one-action.html' title='In Struts, one page means one action'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-1299940298004071684</id><published>2008-03-12T03:01:00.001-07:00</published><updated>2008-03-12T03:14:18.183-07:00</updated><title type='text'>Learning generics by collecting arrays</title><content type='html'>&lt;p&gt;So you have an array of objects. These objects are more than likely things that have properties. And life would be great if you could just get an array of all the values of just one of the properties of those objects.&lt;/p&gt; &lt;p&gt;Why would this be great? If these objects happen to map to a database instance (like entity beans), chances are each one of those objects has a long identifier, with a field probably named &lt;code&gt;id&lt;/code&gt;, with an accessor method &lt;code&gt;getId&lt;/code&gt;. My goal would be to do something like this:&lt;/p&gt; &lt;p&gt;&lt;code&gt;List&lt;myobject&gt; objectList = //...&lt;br /&gt;Collection&lt;long&gt; ids = collect(objectList, "id");&lt;/long&gt;&lt;/myobject&gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;As it turns out, this was not very difficult to pull off. And behold, there is something very similar in a project called the &lt;a href="http://jga.sourceforge.net/" mce_href="http://jga.sourceforge.net/" title="Java Generic Algorithms" target="_blank"&gt;Java Generic Algorithms&lt;/a&gt; library. You can use the &lt;code&gt;transform&lt;/code&gt; method with the &lt;code&gt;GetProperty&lt;/code&gt; functor to achieve something very similar. The problem is that the &lt;code&gt;GetProperty&lt;/code&gt; doesn't work quite right. So I rolled my own.&lt;/p&gt; &lt;p&gt;One first part of the API is answering the question, "what should I call"? I am assuming the following things:&lt;/p&gt; &lt;ol&gt;&lt;li&gt;You are thinking in terms of a field name, not the accessor function format, so I would always think "I'm getting the &lt;code&gt;id&lt;/code&gt;" not "I'm getting the &lt;code&gt;Id&lt;/code&gt;", and the case sensitivity matters.&lt;/li&gt;&lt;li&gt;There is likely a public accessor called &lt;code&gt;getFoo&lt;/code&gt;, and if &lt;code&gt;foo&lt;/code&gt; is a boolean, it might be called &lt;code&gt;getFoo&lt;/code&gt;, or it might be called &lt;code&gt;isFoo&lt;/code&gt;. Depends.&lt;/li&gt;&lt;li&gt;If you didn't write an accessor, you're probably thinking this is some kind of C-style struct, so assume the field itself is accessable.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Here's my breakdown of this in a function I called &lt;code&gt;reflectSimpleField&lt;/code&gt;&lt;/p&gt; &lt;pre&gt;static private &lt;t&gt; T reflectSimpleField (Object object, String field, Class&lt;t&gt; returnClass) throws NoSuchFieldException&lt;br /&gt;{&lt;br /&gt;Object reference = null;&lt;br /&gt;Class klass = object.getClass();&lt;br /&gt;&lt;br /&gt;// check for accessor method in the form getField or isField.&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;  reference = reflectAccessorMethod(object, klass, field, "get");&lt;br /&gt;}&lt;br /&gt;catch (NoSuchMethodException ex)&lt;br /&gt;{&lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;    reference = reflectAccessorMethod(object, klass, field, "is");&lt;br /&gt;  }&lt;br /&gt;  catch (NoSuchMethodException ex2)&lt;br /&gt;  {&lt;br /&gt;    reference = reflectField(object, klass, field);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return (T)reference;&lt;br /&gt;}&lt;/t&gt;&lt;/t&gt;&lt;/pre&gt; &lt;p&gt;Reflecting the individual methods and fields were pretty simple, you just have to figure out the name you're going to use, and if it isn't there, I decided I would throw &lt;code&gt;NoSuchFieldException&lt;/code&gt;.  This isn't a &lt;code&gt;RuntimeException&lt;/code&gt;, which is a little weak, IMO. (In a production system, I might change that, but this is one of those coding excercises.) In general, there were too many damn exceptions being thrown anyway, which makes the API really messy:&lt;/p&gt; &lt;pre&gt;static private Object reflectAccessorMethod (Object object, Class klass, String field, String accessor) throws NoSuchMethodException&lt;br /&gt;{&lt;br /&gt;Object reference = null;&lt;br /&gt;&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;  String methodName = accessor + capitalize(field);&lt;br /&gt;  Method method = klass.getMethod(methodName, (Class[])null);&lt;br /&gt;  reference = method.invoke(object, (Object[])null);&lt;br /&gt;}&lt;br /&gt;catch (IllegalAccessException ex)&lt;br /&gt;{&lt;br /&gt;  throw new NoSuchMethodException("rethrowing IllegalAccessException as NoSuchMethodException :" + ex.getMessage());&lt;br /&gt;}&lt;br /&gt;catch (InvocationTargetException ex)&lt;br /&gt;{&lt;br /&gt;  throw new NoSuchMethodException("rethrowing InvocationTargetException as NoSuchMethodException :" + ex.getMessage());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return reference;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static private Object reflectField(Object object, Class klass, String fieldName) throws NoSuchFieldException&lt;br /&gt;{&lt;br /&gt;Object reference = null;&lt;br /&gt;&lt;br /&gt;try&lt;br /&gt;{&lt;br /&gt;  Field field = klass.getDeclaredField(fieldName);&lt;br /&gt;  reference = field.get(object);&lt;br /&gt;}&lt;br /&gt;catch (IllegalAccessException ex)&lt;br /&gt;{&lt;br /&gt;  throw new NoSuchFieldException("rethrowing IllegalAccessException as NoSuchFieldException :" + ex.getMessage());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return reference;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static String capitalize(String string)&lt;br /&gt;{&lt;br /&gt;return string.substring(0, 1).toUpperCase() + string.substring(1);&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;Finally, I came up with two variants of my original idea for &lt;code&gt;collect&lt;/code&gt;. The first, &lt;code&gt;collect&lt;/code&gt; was intended to also be usable in a &lt;code&gt;for&lt;/code&gt; loop, like so:&lt;/p&gt; &lt;pre&gt;for (Long id : collect(objects, "id", Long.class))&lt;br /&gt;{&lt;br /&gt; // do something with id&lt;br /&gt;}&lt;/pre&gt; &lt;p&gt;This required adding the return type as an argument.&lt;/p&gt; &lt;p&gt;Just for kicks, I also came up with a specialized function for returning a &lt;code&gt;List&lt;/code&gt; instead of something &lt;code&gt;Iterable&lt;/code&gt;. This saves you from actually having to copy values just in case you don't want them.&lt;/p&gt; &lt;p&gt;My wack at these two functions:&lt;/p&gt; &lt;pre&gt;public static &lt;t,r&gt; Iterable&lt;r&gt; collect (Collection&lt;t&gt; collection, String field, Class&lt;r&gt; returnClass) throws NoSuchFieldException&lt;br /&gt;{&lt;br /&gt;ArrayList&lt;r&gt; list = new ArrayList&lt;r&gt;();&lt;br /&gt;for(T item : collection)&lt;br /&gt;  list.add(reflectSimpleField(item, field, returnClass));&lt;br /&gt;return list;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static &lt;t,r&gt; List&lt;r&gt; collectList (Collection&lt;t&gt; collection, String field) throws NoSuchFieldException&lt;br /&gt;{&lt;br /&gt;Class&lt;r&gt; returnClass = null;&lt;br /&gt;ArrayList&lt;r&gt; list = new ArrayList&lt;r&gt;();&lt;br /&gt;for(T item : collection)&lt;br /&gt;  list.add(reflectSimpleField(item, field, returnClass));&lt;br /&gt;return list;&lt;br /&gt;}&lt;/r&gt;&lt;/r&gt;&lt;/r&gt;&lt;/t&gt;&lt;/r&gt;&lt;/t,r&gt;&lt;/r&gt;&lt;/r&gt;&lt;/r&gt;&lt;/t&gt;&lt;/r&gt;&lt;/t,r&gt;&lt;/pre&gt; &lt;p&gt;If you find this stuff interesting or useful, feel free to cargo cult all you want. I'm curious to see how it compares with other things out there in the wild, and mostly, it was a great way to get used to some Java generics.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-1299940298004071684?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/1299940298004071684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=1299940298004071684' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1299940298004071684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/1299940298004071684'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/learning-generics-by-collecting-arrays.html' title='Learning generics by collecting arrays'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-8978557471266006324</id><published>2008-03-12T03:00:00.001-07:00</published><updated>2008-03-12T03:14:09.805-07:00</updated><title type='text'>Eclipse is dead! Long live Eclipse!</title><content type='html'>&lt;p&gt;I love Eclipse! I hate Eclipse! I some ways it's a left-handed tool, but others it's completely right.&lt;/p&gt; &lt;p&gt;&lt;img src="http://tristanhunt.com/wp-content/uploads/2007/11/love-hate.jpg" mce_src="http://tristanhunt.com/wp-content/uploads/2007/11/love-hate.jpg" alt="Love and Hate Image" /&gt;&lt;/p&gt; &lt;h3&gt;What I love about Eclipse.&lt;/h3&gt; &lt;p&gt;&lt;b&gt;It builds and tells me my errors as I write.&lt;/b&gt; Used to be, you'd stare at your code, and then pray it would compile when you'd type &lt;code&gt;make&lt;/code&gt;. And if it compiled, hey, you were almost ready for testing!&lt;/p&gt; &lt;p&gt;&lt;i&gt;Now, as soon as I hit save, it's ready for testing.&lt;/i&gt;&lt;/p&gt; &lt;p&gt;&lt;b&gt;It writes the stupid stuff for me.&lt;/b&gt; Let's face it, the Java language is a bit wonky. One retarded aspect of Java are accessor methods. We need them, but they are usually very simple methods. This means it's very easy to make a typo if you're not careful. With Eclipse, there are all kinds of methods that autogenerate the stupid stuff, and let's me focus on &lt;i&gt;my logic&lt;/i&gt;.&lt;/p&gt; &lt;p&gt;&lt;b&gt;It has a debugger.&lt;/b&gt; I can't believe this is still an issue, but after years of working with debuggers in C+, I couldn't find a satisfactory non-IDE Java debugger. Holy crappy command lines, Batman!&lt;/p&gt; &lt;h3&gt;What I hate about Eclipse&lt;/h3&gt; &lt;p&gt;&lt;b&gt;Grep is better at finding things.&lt;/b&gt; I've given up on the Eclipse search functions. I keep a terminal open and use &lt;code&gt;grep&lt;/code&gt;. It's far more faster. (Of course, I still avoid using &lt;code&gt;find&lt;/code&gt;, more out of sheer anger over it's interface than anything else.)&lt;/p&gt; &lt;p&gt;&lt;b&gt;I can pick APIs too quickly without reading about them.&lt;/b&gt; Eventually, I will code on the monitor I have at home, which is a lovely 30" cinema display. With this size of display, I can select a method, and have it's java doc display automatically in a separate window. Without this size of monitor, however, it is hard to manage the screen real estate.&lt;/p&gt; &lt;p&gt;So you end up doing the following: type, use autocomplete, and pray. Yeah, you could say &lt;i&gt;stop using autocomplete, bitch&lt;/i&gt;. But it saves way too much typing. Go ahead, try to do it, you can't stop. Autocompletion is seriously the crack cocaine of programming.&lt;/p&gt; &lt;p&gt;&lt;b&gt;It's too F-in' big to be my one-and-only.&lt;/b&gt; I still use vim. Why? Because when I'm poking around on one of our systems that's misbehavin', I often have to edit something minor. Or hell, just edit an XML file. Opening that file in Eclipse requires havin' it local, and havin' the right plugin, and havin' enough RAM free that most citizens will only acquire in their latest and greatest machines.&lt;/p&gt; &lt;p&gt;Opening that file in VIM means I don't jack the server performance, and I can do it remotely. Sorry, but I can't live my entire (electronic) life inside an IDE. Sometimes you have to wander into the wild wooly wilderness of a terminal session.&lt;/p&gt; &lt;p&gt;&lt;b&gt;Configuration still sucks&lt;/b&gt; So we have development going on in multiple branches. I want different workspaces for each branch - that's all my hardware can handle at once. This means I have to &lt;i&gt;reconfigure Eclipse with my settings for each damn workspace&lt;/i&gt;. What... the hell? Why can't I tell it my favorite font once, and then for each new workspace, it just uses the one I love?&lt;/p&gt; &lt;hr /&gt;The image title is &lt;a href="http://www.flickr.com/photos/96679304@N00/100434862/" mce_href="http://www.flickr.com/photos/96679304@N00/100434862/" title="Love and Hate Image"&gt;Love and Hate&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-8978557471266006324?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/8978557471266006324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=8978557471266006324' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8978557471266006324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/8978557471266006324'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/eclipse-is-dead-long-live-eclipse.html' title='Eclipse is dead! Long live Eclipse!'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7611869677320175211.post-2073862262952024195</id><published>2008-03-12T02:58:00.000-07:00</published><updated>2008-03-12T03:14:01.140-07:00</updated><title type='text'>Writings should live</title><content type='html'>&lt;p&gt;Why are most things I read on the job undead? Why are documents made in software companies like zombies?&lt;/p&gt; &lt;p&gt;&lt;img src="http://tristanhunt.com/wp-content/uploads/2007/11/zombie-walk-kids.JPG" mce_src="http://tristanhunt.com/wp-content/uploads/2007/11/zombie-walk-kids.JPG" alt="zombie kids" /&gt;&lt;/p&gt; &lt;p&gt;Too often, they are born premature, they live quickly, then are dead before you know it. And not given a proper burial, they wander around, in our electronic halls of e-mail inboxes and obsolete web pages, looking for brains.&lt;/p&gt; &lt;p&gt;How else can I describe reading one of these things? You stare at the document, and usually&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Some things are detailed, but don't really seem part of anything&lt;/li&gt;&lt;li&gt;Some things are vague, but stated as if they are obvious to everybody&lt;/li&gt;&lt;li&gt;Some things are obsolete&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;As you read it, half of your attention span turns to answering questions rather than learning material. These questions are often important, like&lt;/p&gt; &lt;ul&gt;&lt;li&gt;What does this mean?&lt;/li&gt;&lt;li&gt;Wait, didn't we agree to remove this feature 3 weeks ago?&lt;/li&gt;&lt;li&gt;How important is it to have our widget be resistant to power failures if the user can simply break it by uploading the wrong file?&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;There are many problems here. Bad writing. Bad publication. Bad. Bad. Bad.&lt;/p&gt; &lt;h3&gt;Easy things to help avoid document zombiefication&lt;/h3&gt; &lt;p&gt;&lt;b&gt;Send links, not attachments.&lt;/b&gt; Host the latest version of a document somewhere. Then people should just always just jump to the latest rather than having to search their inbox.&lt;/p&gt; &lt;p&gt;Yeah, you can keep old ones around for posterity. But 9 times out of 10 you just need the latest version of whatever.&lt;/p&gt; &lt;p&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Write with meaning.&lt;/span&gt; When a concept is vague, make sure it sounds vague in writing. When it is not, use strong terms. A vague phrase: "it probably provides 10 feeds". An exact phrase: "it will provide 10 feeds".&lt;/p&gt; &lt;p&gt;This helps scheduling in a planning document. Vague things should be investigated before being done. Exact things can be put off.&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/p&gt; &lt;p&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Version with a date.&lt;/span&gt; There is absolutely no reason why your document shouldn't come with a date. You can use revision control systems, but I tend to find dates more universal, and easy to understand. Fancy numbering and variation schemes tend to muddy the water.&lt;/p&gt; &lt;p&gt;I have never, ever, solved a documentation problem by saying "what was the difference between version 3 and 4 of this document"? Instead, I think, "what changed the last 2 weeks"?&lt;/p&gt; &lt;p&gt;&lt;b&gt;Think like a journalist.&lt;/b&gt; Most technical documents would be far better if they started with the big important stuff and then weaved in details later.&lt;/p&gt; &lt;p&gt;It's about context with your details. You need to know the why, not just the what. The who, the when, and the where are often not as interesting in software, but it can be.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7611869677320175211-2073862262952024195?l=tristanisacodemonkey.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tristanisacodemonkey.blogspot.com/feeds/2073862262952024195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7611869677320175211&amp;postID=2073862262952024195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/2073862262952024195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7611869677320175211/posts/default/2073862262952024195'/><link rel='alternate' type='text/html' href='http://tristanisacodemonkey.blogspot.com/2008/03/writings-should-live.html' title='Writings should live'/><author><name>Tristan</name><uri>http://www.blogger.com/profile/00710690769973322961</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://bp0.blogger.com/_V1IhaG-_jZM/R9esgDlTdnI/AAAAAAAAAZU/6uakydQUuoo/S220/HeadShot2_250.gif'/></author><thr:total>0</thr:total></entry></feed>
