Sunday, July 20, 2008
Unit testing - prefer messages
Posted by
Dave Solomon
at
6:08 PM
0
comments
Labels: software development, unit testing
Sunday, July 13, 2008
Mea culpa
When I first started writing .Net code, I was all about implementing IDisposable because I figured that the GC wouldn't be as smart, as fast, as efficient as the stuff I could write. I mean, sure - they optimize for the general case, but who knows better than I do just when to free memory and resources? Not some jackhole Microsoft programmer, amiritepeople?
Since those were the heady days of VC and no clients demanding things change yesterday, I actually spent half a day working with the clumsy spike I'd slapped together and let it fly - it worked well enough under load, so I was happy. Then I ripped out my destructor and let it roll again - I was figuring I'd see the CPU thrashing as .Net's garbage collector did its thing, working on the general, sub-optimal, case it was written for. No egghead knows better than I do how and when this should run!
Except that there was no difference that I could see. If anything, run time was a little faster and memory overhead was a little lower. I mean, probably statistical noise faster and lower (that Excel spreadsheet's been lost to the river of time at this point), but that was a pretty well-defined zenslap moment for me.
I thought about it a little and realized that oh yeah - that garbage collector. I'm not allocating memory either. People way smarter than me have already implemented a garbage collector so I don't have to worry about allocating and freeing memory on the fly. The bold promise of distilling your codebase down to actual business logic rather than bookkeeping allocations and all that.
It's a solved problem, so why am I solving it again, only invariably worse this time? Maybe it was written for a "general case" (whatever the fuck that means because I obviously can't defend it) but it was a pretty good general case.
This all came flooding back to me on Friday. I'm working on a bit of code - an object-XML mapper. This isn't as stupid as it sounds (I hope), honest. It's running, well. Not so good. I mean, it does what I want it to, just way way way slower than I want it to run.
One of the "optimizations" I made was ripping out a lambda expression iterating over a singleton (I know, I know) - I figured that there ain't nothing faster than a hand-rolled for loop with a break condition... right? But I wasn't making any headway with the other two offending methods after re-ordering my if block, so I decided that I might as well, you know, test it out to see how it performed.
I didn't check memory this time around, but damn if it wasn't just as fast as the for loop. Maybe a little faster, even.
Again, the zenslap - the framework's made by people way smarter than I am. I need to count on them to have done their homework and made stuff easy to use and scary fast.
Stop reinventing the wheel. I'd bark at co-workers who tried to roll their own second-rate mechanism for mapping objects to an XML hierarchy we don't control, so why am I confident in my ability to roll my own iteration loop? On some level, doesn't it make sense that smart people who get paid to work on iterators might find a way to wring a little more out of them?
It's not easy discovering that something so simple that you've taken for granted for so long (a for loop!!!) is halfway obsolete, but it's liberating once you get over yourself and embrace it.
So here's to you, whoever implemented .Any() - you did a helluva job. Way better than the jackass who shat out [DebuggerStepThrough].
Posted by
Dave Solomon
at
10:38 PM
0
comments
Labels: .Net, software development
Tuesday, June 24, 2008
Software management - The Oregon Trail model
OK, forget reading Peopleware and The Mythical Man-Month. If you want to manage developers, there's only one - no wait! - two things you need to do.
- Be born in the late 70's/early 80's
- Play The Oregon Trail on an Apple II
That's it! If you can make it to the end of the trail, you know everything that you need to know about successfully managing software, so go successfully manage the shit out of a project or three!
Wait. You're still here? I should go on? OK.
You have your hunters, guys who strike out in hopes of lucking into a bounty of delicious, delicious meat. Speaking authoritatively as someone who's never hunted or caught anything of note while fishing, I'll say that a good hunter operates off of instinct, innate ("animal") intelligence, skill and luck. Legends tell the tale of the hunter who struck off into the deep of the jungle with nothing but a knife and came back carrying a gorilcopterous (work with me people, I live on the edge of the tundra) or some big tasty animal that lives in the jungle0.
Then you've got the gatherers who plant crops and (hopefully) eventually got to harvest the bounty. It takes a deep investment in time and resources to see a field from seed to harvest, and even then there's any number of natural disasters that can beset you along the way. This isn't to say it's easy. On some level, we all understand how plants grow. They eat sunshine, water and carbon dioxide and crap oxygen and chlorophyll, right? On another level, there's a hell of a lot that goes into it - seeds cost money. Machines to till and harvest cost money. Irrigation costs money. You've got to know when to plant. What to plant. When to rotate your crops.
Developers are an impatient bunch, which means that we don't generally have the patience for the small, far-off, rewards and repetitive work that it takes to grow a crop. But we get hungry, so we're going to strike out and hunt, hoping to bag the next seminal idea. OK, it's probably not a seminal idea by any stretch of the imagination, but for a brief moment in time, you really feel like you've crafted something brilliant and have found something more than you could have hoped for, something that's somehow bigger than you could have imagined.
But eventually, the quarry of the once-fertile plains and forests of imagination!!!!!!! will run dry. Your hunters will pick up on the fact that there's no more ideas to be hunted, only tedious fields that demand regular, monotonous maintenance, and they'll migrate elsewhere. This is all well and good, but every now and then wouldn't you like a steak to go with those potatoes?
But again, I get ahead of myself - let's take a step back to see what a dated educational game has to do with any of this.
In The Oregon Trail, you're in charge of seeing a family1 make the trip on the Oregon Trail from Missouri2 to Oregon3.
You notice how they emphasize "try"? It's because of a simple fact that you don't want to admit to yourself - no matter how simple it seems like it should be, that shit is fucking hard to accomplish4. To the outsider, it's as simple as getting from point A to point B. That's really all there is to it, right?
Go figure - there's nuances that you hadn't even considered when you started out. Could this mean that you'll eventually discover that there are nuances upon nuances? This makes the decision of something that seems as simple as when to leave a dizzyingly difficult one.
Leave early and you'll be freezing and making slow, painful, progress5. Leave too late and you'll face the unenviable, super-difficult, task of over-wintering6. Face it - you've tried to make a trip like this before and you've probably still got the bruises and scars from the last one, but you keep telling yourself that this time will be better.
You need supplies to make the trip. Oxen to move your cart7, clothing to keep you warm8, food9, replacement parts10... and bullets for hunting11. And naturally, you're constrained to a budget that's tighter than you'd like.
Making matters worse (you mean it gets worse?), you're probably making the trip with a bunch of egomaniacal (wo)man-children. I mean, we all strive to be egoless in our tasks, but we all take an undue amount of pride in an elegant solution and invariably take it the wrong way when someone points out the glaring flaws in our implementation. Or maybe I'm the only one, who knows?
But... about those supplies. Some of them are effectively interchangable. If you've got a good hunter (you have a good hunter, right?), you can exchange bullets for food in the wild - that's the biggest bang (pun unintended) for your buck. Then you can trade food for just about anything else you need along the way12. You can run pretty lean-and-mean13 but you need enough food to keep your bellies full.
You're going to want to hunt (because it's fun!), but not every hunt can be a winner.
What can I tell you? Times is tough out there. Even with a gang of elite hunters firing on all cylinders and a pile of food (ideas!) as high as the eye can see, sometimes nature just doesn't smile on you. As in any pursuit, it's entirely seemly that you can do everything right and still fail.
Not only are times tough as hell, but you're fairly constantly reminded of your own failures and the failures of others in a big, somber way.
Is there any wonder why Einstein up there didn't make it? Wasn't cut out for it. You can't teach people how to hunt - they're born with it or they'll never get it, now matter how blue in the face you get trying to explain it to them.
The trip itself? It's possible that your people are entirely happy in Missouri right now. You can crack that whip all they want, but if you don't instill a powerful longing in them to reach the promised land of Oregon, you'll never make it. In fact, you might find yourself unwillingly invited to a Donner Party. And not to ruin the surprise, but everyone at the party will be eating but you.
0. you know, your run-of-the-mill whispered about in legends superhacker
1. your development team
2. the start of the project
3. the project's end (you do have a concrete end-point in mind, right?)
4. #9 on Software's Classic Mistakes - Wishful Thinking
5. sometimes you have the toolchain you need to complete your project, other times you're kind of winging it as you go along - bootstrapping too much of your own technology stack only shrinks the chances that you'll ever make it
6. running out of funding
7. computers
8. technology stack - compiler, language, yadda yadda
9. ideas
10. source control and the other niceties of modern development (build server, unit tests)
11. ain't nothing more dire than running out of bullets - there's no metaphor here
12. dumbest way of describing the point of open source ever?
13. agile??!?
Posted by
Dave Solomon
at
10:53 PM
0
comments
Labels: management software, narcissism of minor differences, software development, wtf
Saturday, June 21, 2008
This week's dispatch from the seventh circle
"I don't understand why FxCop is complaining about this static property on a static class."
"Probably because static classes are stateless, so exposing properties doesn't make a whole lot of sense."
"Yeah, but I want to expose properties so that a caller can set the values that they want."
"But that's really not such a great idea. Either overload methods with the parameters you're trying to read from properties or let callers instantiate the class and set the properties themselves."
Why did I find this so disturbing? Probably because it took two days for the chap to make the change.
The other reason I found it disturbing? I did the math in my head and decided that there would be approximately zero point in trying to get him to understand why globally mutable state can quickly lead to misery (false negatives/positives) when you're dealing with the MTA that NUnit runs in.
Posted by
Dave Solomon
at
11:26 PM
0
comments
Labels: narcissism of minor differences, programming, software architecture, unintentional irony, wtf
Tuesday, June 17, 2008
Design patterns as religion
I've come to an understanding of and guarded appreciation for design patterns.
They're not at all useful in and of themselves, but they are useful at a level of abstraction - they give us a common language to explain our implementations with ("...so basically, the model objects are just kind of structs and we cajole them into doing interesting things by Decorating them...") and, more importantly, they let us learn about the boundaries, limitations and realities of the design space that we're working in by revealing the problems that get solved and solved again and again and... you get the picture.
If your objects start to look like a pattern, it's a curiosity. Patterns were discovered "in the wild" to begin with, so don't tire your arm out patting yourself on the back for rediscovering one. No gold star for knocking out a Flyweight, sorry. On the flip side, don't flagellate yourself when you realize that you've implemented a Singleton - if it emerges from solid design, why would you throw it away?
It doesn't matter if you're blindly implementing a pattern or blindly rejecting an implementation because it looks like a pattern that someone on the Internets doesn't like - it's cargo cult programming either way.
Design patterns are there and were there before anyone started looking for them or writing about them. People have classified subroutines as a design pattern - like other "patterns", this isn't a reason to use or not use them, it's further establishing where they come from - introspection on how we make programs work. There's something to be said for taking a stroll through that meta-field, but I don't linger there too long. Most of the time.
All this is a long-winded way of explaining why my monocole absolutely popped out the other day at work. I was busy doing something or other at the time (transliterating objects to an XML format I don't control using unhealthy amounts of Reflection, I think?) when I saw one of the other developers explaining something or other. I popped out my earphones to catch the explanation midway through.
He had the Head First Design Patterns book open on his desk and was walking through his ideas.
"Why are we bothering with inheritance? The domain model you guys put together is too convoluted and doesn't work in this case."
"If there's something we missed, it's not like it's set in stone. If it's really hairy, we can walk through it and see if there's some reasonable way to get it factored in and if there isn't, we can go further back and see how we can make the model work for us."
"The <I forgot already!> pattern says that you can use hashtables to store the data for your objects, rather than inheriting from a base class."
"Well, yeah. But think about why we use objects and I think you'll see why that's probably a bad idea."
...
"For our model objects, we're use them not only to encapsulate associated data but to make it easily discoverable - when you're working with them internally, it may seem pretty convoluted that your parent base Foo class has property Bar that's exposed by a child Buz object (which has more properties and methods from the IBaz interface that Foo implements), but that's all readily discoverable by anyone who's consuming a Buz object. They could kind of care less how it works, they just need access to have access to its state and be able to send it messages to have it do interesting things."
He was not sold by my explanation and the discussion closed in on arguing (something about our respective tones and the other developers in the office getting quiet and looking uncomfortable) about whether it makes more sense to have the classes representing data internally like a struct or as a hashtable - I scoffed (and still scoff!) at the idea but told him that he was the one implementing it, so if he had a good reason for properties exposing a hashtable behind the scenes, I was fine with it.
I started to look up hashtables and figure out whether he was really on to something or whether my gut feeling that it was adding an unnecessary level of complexity to the design (OK, I was premature optimizing in my head and wondering about the relative weight of sparsely-populated hashtables, too) when, a few minutes and pages in the book later, he announced to another developer in the office (!!!) that the book said that you should only use hashtables when you're dealing with a lot of inheritance and a lot of properties.
I don't think there was any amount of selling I could have done to get him to walk away from the curious implementation - it was design pattern as dogma. Blinders on, reject facts, implement on faith and The Pattern will see you through.
I'm still trying to figure out if I was wrong (probably, yeah) for knee-jerking away the use of hashtables or not. I'll let you know when I turn that page.
Posted by
Dave Solomon
at
10:30 PM
0
comments
Labels: code smell, narcissism of minor differences, software architecture, unintentional irony
Thursday, May 29, 2008
Aging codebases, a postscript
Ron Avitzur's comment on my post wondering aloud as to whether code can age gracefully done tickled my brain. I mean, I think about code a lot. I think about software and making software a lot.
But for some reason, I hadn't ever pondered how truly strange software is - if you look at it a certain way, it's never truly broken. It may be old, it may be ungainly, but it will always do something.
Sure, the hardware that it ran on may disappear or cease to function and the compilers may evaporate... but that doesn't mean that the code's broken. It just means that you've got a little work ahead of you porting that software to the latest and greatest hardware and toolchain and bingo bango your software's back from the dead.
Of course, there's always the question of "should we?". Times move on and inevitably reach a tipping point where maybe it makes sense to let that sleeping codebase lie.
Steve McConnell refers to this as a "doghouse problem" - when it comes to building a doghouse, everyone has an opinion on what color to paint it, what the roofing tiles should be, the exact dimensions, yadda yadda because it's a problem so eminently tractable that we can easily wrap our heads around it. Hell, it's within the realm of reason that we could build it ourselves. We have a pretty good idea of what it'll cost to make and how long it'll take.
But ask us to price the cost, materials, resources and time required to build a skyscraper and... good luck on estimating that one. How hard can it really be though? They're both buildings, right?
A couple of real-world buildings brought this into clear focus for me.
First is an old mill building not too far from where I live. For the years I've been here, it's been uninhabited - a hulking, crumbling mess of a building. 100 years ago, I'm sure it was a bustling center of commerce, an economic powerhouse. Now it's gone past creepy and into sad. It's being demolished to make way for... I don't know. Probably another drug store or something.
Second is This Old House, or what's left of it. There was a house there once, nothing here now but the bones of the old building rising like so many dinosaur bones from the ground.
Either one of them could be broken down as much as possible to be built back up to work again, but it's the house whose bones will remain in place while the mill's ground into the dust. Looking at it, the possibility of fixing it piecemeal doesn't even enter into your mind - it's over a hundred years old, frigging brick and huge. The house, on the other hand? Over a hundred years old, but the frame and chimney are still sound - we can rebuild around them, putting the walls and stairs and windows in different this time.
When looking at open source projects, it's not uncommon for one of the mentioned features to be their "clean codebase" - on some level, this is kind of laughable because who but a geek gives a shit what the code looks like as long as it runs well enough and does something useful for you?
On another level, it shows that there's people who have taken pride in their work, that fix broken windows, and have a vested interest in making sure that a year from now, it will have crumbled less than that mill building.
Think about building something that will stand the test of time and it just might. Make it concise and useful enough and who knows? Its framework just might live a decade or so.
I hope to someday build something so good that I won't be able to hear the lamentations of the developers responsible for supporting it because I'm busy watching Wheel of Fortune too loud or whatever it is that incredibly old people do. Did I mention that I'm watching The Wheel in my rocket car? Because ideally, I'm incredibly old, reasonably well-to-do and the future finally delivers on its promises.
Postscript
I biked by the house again earlier this week. Nothing remained.
I still hold out hope for a rocket car-filled future.
Posted by
Dave Solomon
at
8:29 PM
0
comments
Labels: picture time, software development
Monday, May 5, 2008
SQL Server, we need to have a talk
So I'm working on normalizing the database the other day. I want to move some columns from a table that I'm about to make vanish to another table and I can't remember the syntax for it. I should have hit up The Google (the SQL syntax for adding tables/columns to tables really is dead simple) but instead I was extra lazy and fired up the table designer inside of SQL Server Management Studio and scripted out the changes to the tables.
I finish that up, finish modifying the stored procedures, everything looks good. I run the SQL script, fire my black box tests against it.
Hmm. Things ain't working right. The column that I just defined to hold a decimal is causing problems. I check my stored procedures to make sure I'm not doing anything insane, check my business logic to make sure I didn't bork anything there, it all looks good.
I take what I'd just scripted and distill it down to its essence.
create
table scratch
(
percentage decimal (18,0)
)
go
insert
into scratch
values (0.5)
select *
from scratch
Not-so-curiously, the scratch table now contains a lone value of 1.0.
When you look at it, it's obvious what went wrong here - I just defined a decimal with zero digits of decimal precision. Or, as I like to call it, a fucking integer.
I obviously biffed in the first place, but this left me scratching my head - why on earth would you make the default for a decimal (effectively) an integer?
As an added bonus head scratcher, there's no warning about the loss of precision/silent rounding when you store the value in there. I don't know what should happen there (do you want an exception because of that sort of thing?) but I do know that it rhymes with "leaky abstraction." I'm not an RDBMS guru, people.
Times like these, I understand why it's regarded as best practice to treat money as two integer values (dollars and cents!) - databases are a bit funky when it comes to storing out your decimal values.
digg
reddit
del.icio.us
