Friday, June 29, 2007

Maggot therapy for code

Showing the project owner what I'd done, she had one big question for me. "You started from scratch?" Accompanied, of course, by an incredulous look. So much for subtext.
But let's back up for a second.
So I spent a few months last year building a bunch of pieces of functionality to support a new business need. Late (but under-budget! (since I was the only person working full-time on it)) and hairy as anything, but it more or less works at least some of the time. Hairy enough that when asked "can it do this?" most of the time I roll my eyes and groan and tell them "it's complex and fragile and I'm not sure so I'd guess it'll take me a week" and then sometimes I've already built the functionality. I laugh at Peoplesoft for being Rorschach software and look - I've built some of my own.
As time's gone on, I've gone back in and refactored bits and pieces and have new objects that branch out of the old (that are probably better, honest!). I inherited one subset of these associated objects from a recently-departed coworker.
I've made enough bugs of my own in the past that I worry about other people interacting directly with my stuffs and making me look like a jackass that I've done code audits of the work, so I knew what I was in for.
Loop unrolling.
Bona fide magic numbers. "OK, so there's codes of 3, 4 and 7 that I can apparently pass down from the presentation tier. These are enumerated in the business tier, right? Guess not. Data tier? No. Oh - they drive branching in the stored procedure. Brilliant."
Leaky abstractions. As in the presentation tier sometimes calling business objects, others the data tier, still others the database. Plus handling business logic in the presentation tier.
Code paths in the business tier and database that weren't called in the database - so stored procedures and methods that were essentially dead code.
I'd already given the work a vote of no confidence, so when it was handed to me, I went with my gut - tactical nuke.
But there was that nagging voice in the back of my head. "Good job, Dave - way to go with Not Invented Here Syndrome. Worse, you're exhibiting It Has Been Invented Here, But I'm Too Lazy To Read The Fucking Code So I'll Just Do It All Over Again Syndrome." Couldn't I have spent this week doing something more productive, just fixing the bugs that were logged and moving on, rather than re-doing it pretty much from scratch, virtually guaranteeing that I've introduced more in its stead?
I guess there's that, but given all the code smells... and honestly. Loop unrolling? I've only seen shit like that on The Daily WTF. Who does that shit? Magic numbers? Yow. I've sort of taken an ambivalent stance on them in the past, but when "magic number" means "you have to open up the stored procedure to figure out why the presentation logic passes down this value in this circumstance", I have to scratch my head.
I referred to it as applying a "tactical nuke" when I checked it in, and that too gave me pause. I mean, Joel says you should never completely rewrite it, so you should never completely rewrite it, right?
In medicine (and thank god I don't know this from personal experience), sometimes wounds become necrotic - the tissue's been so damaged that it's no longer living. At times like these, doctors have to become a bit, shall we say, "inventive." I can't think of any other way to describe applying maggots to a human being for medicinal purposes. From the Wikipedia article on maggot therapy (which I'm not linking because fucking eww):

The maggots have three principal actions reported in the medical literature:

  • debride wounds by dissolving only necrotic, infected tissue;
  • disinfect the wound by killing bacteria; and
  • stimulate wound healing.
So calling it a "tactical nuke" was going way overboard. There were bits and pieces in there that worked pretty well. Not the way I would have done them, but I won't argue with working code that works in a reasonably intuitive fashion. So I left the living pieces and excised the suspect ones, the untenable ones.
Two days later, I ultimately think I did the right thing. Two weeks later, after QA's gotten to bang on it, we'll see how confident I am in my skills as Senior Systems Maggot.

Saturday, June 23, 2007

Management smells: don't ask

Let's suppose you're not a programmer. Then let's further suppose you've never known a programmer... yet you manage them. Where do you go to figure out what makes them tick and how to work (OK, deal) with them?
Imagine that all you knew about programmers you learned from Joel on Software and the Jargon File. OK, the latter's really a stretch because t-files are, like, so old. Between reading the two, one would get the impression that programmers are some sort of super-genius prima-donnas dropped in from another dimension with its own bizarro set of social mores.
But in-between the auto-hagiography, you'd find them staring gleefully at some gems, like this tidbit taken from the Jargon File's entry on the SNAFU principle.
True communication is possible only between equals, because inferiors are more consistently rewarded for telling their superiors pleasant lies than for telling the truth.
So have you figured out my clinically-developed, painstakingly-researched advice for you? Talk to your people. It shouldn't be that difficult to figure out. As a manager, do you work with people or do you just crack the whip and shit magically appears? I'm guessing you work with people.
No matter how smart or alien they may seem to be (and when you ask them how it's going, you may get an answer in what approaches moon-man), people all generally share the same sort of motivations. Food in the belly, roof over the head, meaningful work.
The meaningful work thing can be a hard sell - at the end of the day, how many people do you know that get PUMPED up for Industry X? You can sell tickets to a rock show, but you have to bribe people into attending that process improvement meeting with the lure of free lunch (and bottomless cups of coffee). But you can become the meaning for them. Ask them how things are going. If there are any problems that they've encountered. What successes they have. Show interest in what they have to show you, even if it's not much.
If you can't find the time in your busy day to show any interest in what they're doing, don't be surprised when they show a concomitant level of care and interest in the product they're creating for you. Huh. Maybe those semantically-challenged scrum maniacs really are on to something with those daily meetings after all?

Tuesday, June 19, 2007

Seeing the code rather than the product

Unintentional irony is never a pretty thing.
A feller out on that there interweb started off so strong - people who write books or knowingly put code out in the public domain for learning purposes should hold themselves to a higher standard. Amen to that, brother! And the comments invariably start filling up with other folks linking from reddit.com who just have to whip 'em out and show the guy that they know micro-optimization like nobody's business and never mind missing the writer's point (make your code correct and readable, especially if people who might not know any better are going to be learning from it).
And then I took a look at what exactly the writer was railing against - an example from an AJAX book about how to validate credit card numbers in Javascript.
If you could take a moment from furiously hacking away at that ZX-80 assembler solution to the problem, let's jump to the point so you can get back to it.
The book's suggesting something completely fucking insane - client-side validation of credit card numbers - and the most offensive thing about that is that the code's cribbed poorly from a Wikipedia entry?
Back in high school, one of my computer science professors graded starter programs by the "cat on the keyboard" test - subject to random input, will your program gracefully handle it or will it shit the bed?
Rule number one-or-so of client-server development (and sorry, Web 2.0-aholics; it's still client-server no matter how semantic or semiotic or other big words I don't understand-ic you try to make your app sound) is: never trust client input. Their cat could be walking on the keyboard or, when there's money on the line, they could be trying to game the system. Validation of any critical data has absolutely no place being performed on the client-side. My two cent AJAXy solution? Make a web service you can call to handle the server-side validation and then make your AJAXy call to that service when appropriate to see if the credit card number's valid to give the user the immediate UI response you crave.
Don't get so wrapped up in your code that you lose sight of the product and basic common sense. When the music stops, you don't want to be the guy earnestly deliberating about whether to pound that nail with a shoe or glass bottle.
I still haven't gotten the scuff marks and shards out of my wall.

Monday, June 18, 2007

A love letter to the software QA folks of the world

In our twisted little minds, we're fashioning castles out of dirt, breathing life into the imaginary peasants that inhabit the castle and coming up with new and fascinating ways to teach the little people to exercise functionality. Nothing could be wrong with my castle! I built it myself from scratch! Can't you see all the little people in there grinding away just like I taught them to?
But oh! There be a storm brewing! Eventually we have to put down our magic wands and show people just what we've been up to and there's the QA folks. Looking everything over with a discerning, non-paternal, eye and pointing out that I'm just an idiot playing with dirt and by the way that castle you built? The walls aren't up to spec.
Developers and QA operate in an antagonistic relationship. We build things that we're proud of (and if you're not proud of what you're building, you're making their job a hell of a lot easier) and they knock it down. Maybe it's because I'm an entirely reliable source of bugs, but on the whole, it's been mostly mock antagonism. True, I enjoy tagging bugs as "unable to reproduce" or "user error" more than I should, but at the end of the day, I appreciate that there's someone checking what I'm producing and verifying that it's not all wrong.
I'd tell you that I test as much as I can, but if I told you that, then I'd be pretty hard-pressed to explain some of the head-scratchers I've released in the past. Can I blame it on not using test-driven design? Not being agile enough?
Developers get the cool software toys. I've seen a few QA test environments in action and they're pretty execrable, little better than the (super-awesome and totally deserving of the coveted Dave Solomon Seal of Approval) Watir. I can get unnaturally pumped about source control. What else is there to get psyched about in the world of QA software?
We get the cool development methodologies. Test-driven design (jesus christ we're developers pretending to be QA! are we trying to put you folks out of a home?). Agile. Scrum. What do QA folks get? Seriously. I have no idea.
Worst of all, the project timelines. When the specs take too long to get hammered out and development drags on too long because the software's more complex than expected (leaving more nooks and crannies for bugs to fester in), what does your enterprising project manager propose as the solution? Push the release date back? Nah. Just cut the QA cycles short. Sell the sizzle, the quality of the steak be damned.
How many projects will be haunted to their grave by that decision?
So here's to the software QA testers of the world. Despite being forgotten children when it comes to software, viewed as a liability by managers and loathed by developers afraid to eat their own dog food, you somehow manage to persevere and keep the quality up.
Just quit going over my code with a fine-tooth'd comb, willya?

Tuesday, June 12, 2007

Object-oriented management - throw new DevelopmentException();

From what little I can remember of object-oriented design from school, there was next to no attention paid to something that, in my idle thinking, makes OO implementation a whole lot more pleasant - exceptions. There's an analogy to development management in mind here, but to draw it fully I think that I need to lay out how and why I use exceptions in my object design first.

So let's say that you're developing an object. It performs functions that may or may not succeed - how do you communicate failure? At a simple level, there's good old booleans. True/false - the method call succeeded or it didn't.

class Foo {
public int Bar;

public bool Retrieve(out MissingRequiredValueBar) {
MissingRequiredValueBar = (this.Bar == 0);

bool returnValue = false;

// retrieve stuff!

return returnValue;
}

public bool Save() {
return false;
}
}

So this does a reasonable job of telling the whole story... in a simple case. But look at that Retrieve method - we've got two booleans to juggle already - pass/fail, plus an error state that we need to return explaining why things fail.


How do we know that the calling method's checking and working with both of those booleans? Boy, it'd sure be bad if they didn't. And what about Save? They know that returning false means that the data wasn't saved, right?


Furthermore, in a more complex case, I don't think it's hard to see (I'd show you some old code I'm ashamed to have written if I could) that our Retrieve() method's going to metastasize into a hideous mass of parameters. The more parameters you have, the better the odds that you won't have or need one in a calling scenario; method overloading can only take you so far.


Maybe you're thinking of another way out of this jam. Divorce the return parameter from the method call and make it another public property, right? So very wrong.


Do that and you're depending on the person calling your class to have intimate knowledge of how things work ("Oh - first I call Retrieve(), then if it fails I check this other property to see why it failed."). That's a best-case scenario and even there, you're not impressing anyone. In a worst-case scenario, your class has been transformed into an API and the developer on the other end is reaching for a decompiler while promising to punch whatever idiot was responsible in the throat for this godawful code that's been foisted upon them. Not that I've ever made that promise. Objects love loose coupling, remember?


So let's take another stab at this awful example of a class but use exceptions this time.

class Foo {
public int Bar;

public bool Retrieve() {
if(this.Bar == 0) {
throw new MissingRequiredDataException("You need to supply a non-zero value for Bar in order to call Retrieve.");
}

bool returnValue = false;

// do stuff!

return returnValue;
}

public bool Save() {
throw new FunctionalityNotImplementedException("Save functionality isn't working yet.");
}
}

A common critique of exceptions (along with the baffling observation that "throwing them is slow") is that they shouldn't be used as flow control. And you know what? I absolutely agree.


But notice - exceptions aren't being used as flow control, they're being used as cessation control. The exception message is unambiguous as to how to resolve the problem. Whether it's being used as an API or simply as a black-box object by a co-worker, it's clear what needs to be changed in the calling class to avoid the exception.


I used to write classes and have little todo comments littered all over the place. It wouldn't be long before I'd get things bootstrapped far enough along to start testing. Something goes wrong upstream and I spend time debugging to eventually realize/remember that I hadn't implemented that piece of functionality that I was trying to use yet. With exceptions, there's no ambiguity as to the source of the problem.


See how Foo's Save method blows up obviously? Would you prefer spending time figuring out why Foo's data isn't showing up in the database?


If you're not already converted, I imagine that it sounds like we'll be awash in a sea of exceptions, right? While your objects are in development and people are discovering the rough edges, yeah.


But as time goes on, a curious thing starts to happen - you and your co-workers get tired of getting bombarded with exceptions and you start to fix them. Yesterday's exception becomes today's new use case or available error state.


Summing up exceptions, I like them because...



  • Your class knows best when something's gone wrong and should have the ability to call a complete stop to the proceedings
  • You have the ability to provide context to the object's consumer that they obviously need
  • They make it very clear what isn't working with your class as you exercise it
  • The calling class can treat your object as a black box - all it knows is that either their call worked or it blew up and why
  • The calling class has the option to let that exception bubble up to its subsuming classes or to nip it in the bud right there
  • Error states can be misdiagnosed or ignored, exceptions will stick around until the process responsible is fixed

So here's where I make the big leap from merely being a so-so developer to being a clueless, completely inexperienced, hopelessly naive manager-wannabe, but I don't think this is the stupidest assertion I've ever made.


Problems employees face should be treated like exceptions and bubbled up to their manager as soon as possible.


As a manager, you trust that your employees are doing their work, but for the most part, you want to leave them be and have trust that what they're doing is working. In that sense, you're treating them like objects - do you step through every line of code of every class that you call or do you validate the inputs and outputs and take it as a matter of faith that things are working as expected since it looks OK?


When it comes to developers, there seem to be three ways to handle problems that crop up.



  1. "What problem? Don't worry about development going long, we'll just make up for it by cutting the QA cycle a little short. It's not a big deal, because if a component took longer to implement than expected because of the complexity, you can be sure that there won't be too many problems in the finished product that I'm hurrying to make."
  2. "Holy shit I had to open up a debugger today because Dave's Foo class threw an exception when I tried to Save we are going down in a sinking ship."
  3. "I know that I promised you that Foo in two months' time and I've only been on it for a couple of weeks, but I didn't realize how crufty the surrounding portions are. If I have the time to refactor classes ancillary to the new functionality, it'll take longer but should ultimately be more stable. If not, I can shoehorn it in but it likely isn't going to be pretty or stable."

#1 is probably the most common way. People don't like to throw exceptions because at its heart, you're admitting failure when you do. They don't like to cop to being late or not having features complete because again, admitting failure. At best you'll pull teeth and get some status information to work with. But how do you know how severe the problem is or isn't? How can you trust the status they're giving you? At worst, they'll be completely silent about how things are going until the day before the gold build at which point maybe they'll dig deep into their bag of tricks and pull the 24 Hour Push Of Redemption out. It's too late for you to let anyone up the ladder know at this point. Pull teeth earlier next time.


#2 is the sky is falling way - dealing with them, you know that you're going to be awash in a sea of noise. If you listen to the roar, you'll be convinced that the sky is falling too. Then the sun will rise the next morning and over time you'll learn to take what they say with a big grain of salt - you'll implement processes to deal with this constant stream of exceptions. It's perfectly permissible to catch exceptions if you know what you're doing, but when you know what you're doing, you know that there are times when you have to let them bubble up or add context and re-throw them. A doctor's going to be annoyed dealing with a hypochondriac, but even a hypochondriac genuinely gets sick sometimes. Still preferable to being blind-sided by an error state.


#3 is about the best you can hope for (and what I hope my way is) -they don't overreact to problems but at the same time, they don't try to sugarcoat any of the big, project-threatening, hurdles in the way. Figure out what the blockades are in the process and try to resolve them.


Over time, you'll see trending - what exceptions are my employees raising to me most? Chicken Little's saying that the machines aren't beefy enough to keep up? You can probably back-burner it for a while. The Late-Warning, Silent Type's griping about the compile speed of the machines? Time to freshen up that hardware.


You'll react to the exceptions as you need to and (hopefully) resolve them as you see that you need to and can. You'll implement processes to catch the ones you can, to quash the ones that don't matter and to bubble up the ones that are nightmares.


Then again, I'm a developer who thinks of co-workers as objects and problems that a project faces in terms of exceptions. Worst. Metaphor. Ever?

Thursday, June 7, 2007

WTF exactly is wrong with The Daily WTF's site rename?

Ladies and gentlemen, a new pithy tenet of the software development world has been born into life.

Before, we had to slum it with lame old one-or-two-liners like...

The first 90% of development will take 90% of the time. The last 10% of development will take the other 90%.

Perl is write-only.

Java is the new COBOL.

But that's so pre-web! We need to get with the times and have something that goes down easy in our RSS readers! So we have a new one!

The Daily WTF sucks now that the WTF stands for "Worse Than Failure."

Has the quality of the postings gone down? I don't think so. With new editors there and the 3 posts/day that they're churning out, there's bound to be some that don't quite fire on all cylinders, but I get a chuckle or a sad shake of my head out of something most every day there (still). That said, I can't lie and tell you that I'm some sort of sophisticated gentleman. I play video games and laugh at fart jokes so I obviously don't know from quality, plus I might have licked my old Voltron toys to get them to stick together better when I was a kid so I might be a little (OK, a lot) retarded.

Is there really that much in a name or is there more to it? Obviously, I think there's more to it, and here goes.

When it was The Daily WTF and the WTF stood (spoiler alert!) for What The Fuck, it was nothing more than a freak show. Only in the place of the bearded lady and the world's largest horse, we had the programmer who overloaded booleans so he could enumerate FILE_NOT_FOUND! Ha ha ha! They're so much dumber than I am! Can someone around here give me a big high five because I solved FizzBuzz in Erlang the other day? Paula Bean LOL!
Now that it's Worse Than Failure, could it be that it cuts to the quick of that nagging fear that I've got in the back of my head. Maybe it's in yours too... it says things like "I thought this object model was the bee's knees, but have I gone too far? Can anyone but me support it? Could I have done it a better, simpler way?" Things like "What exactly is the point of all this? There's a metric shit-ton of code and tables, but at the end of the day, does anyone appreciate what I've done?" Things like "Is this what I have to show for the last few years of my life on this?"

Or, to paraphrase Morse, "What hath we wrought?"
It hurts to think critically and realize that the system that you've worked so hard on probably should never have been built in the first place. That those pet classes of yours might look like the Sistine Chapel to you, but to the rest of us they're little more than a house-shaped booby trap constructed out of snot, zip ties and duct tape, waiting to trap and maim us in new and unexpected ways each time we brush up against the walls.

That you've taken a rusty, but perfectly servicable, old DOS application and re-implemented it as a spanking new web app with all the fixins (AJAX! MVC and so many other patterns! Multi-threaded!). You see a success, your users see that you've architected a monumental clusterfuck that's so ornery and unusable that they're keeping around their 386s because you've all you've succeeded in is failing their needs miserably.

That a lot of the time, development feels an awful lot like the Red Queen's Race.

Or maybe I'm missing the point altogether and have no clue what the fuck I'm on about. Has the quality really dropped, are Alex Papadimoulis and his associates sellouts (however that would apply) or should Shakespeare have wondered "what's in an acronym?"

Really, is the world a better, happier place because of what you've done? Are people getting more out of your system than they're putting into it? If your system disappeared tonight, would anyone care tomorrow or the day after that? Is it possible that your successes are such untenable messes that they really are worse than failure?

Monday, June 4, 2007

Code Smells - Developer Literacy

Continuing on the subject of interviews, if I could give one piece of advice to people job-hunting, it's this - proofread your resume. It isn't hard, honest. If I could give another piece of advice to developers actively writing code, it's this - proofread it.

When hiring time comes around, I have no trouble whittling down the candidate pool. A middle-of-the-road candidate is getting tossed if their resume has misspellings or glaring grammar errors. My grammar's awful enough that I wouldn't know a dangling participle if it hit me in the face (I know that they're bad, do I win a cookie?) but I do know to avoid tense shifts and other obvious biffs.

But developers write in code, not in English! Surely this is a case of the narcissism of minor differences!

I think it goes beyond that. Misspellings are a code smell for me - if a developer can't be bothered to learn and properly apply the language that they've been speaking for 30 or 40 years, how much faith do I have in their ability to learn and properly apply a language that they've only been "speaking" for 5 or 10 years? So, if I may retort...

If you don't proofread your resume/e-mail, how much faith should I have that you proofread your code?

If you don't proofread your code, how confident do you expect me to be in the fact that you've debugged it?

If you can't be bothered to crack open a dictionary, chances are you won't bother cracking open Google when you encounter a problem. I imagine that you'll instead choose to boldly and blindly rush head-first go into the same tar pit that so many arrogant developers before you have.

I won't go so far as to say that you should go out and get an editor (but the man is on to something), but I will say that you're not showing a whole lot of regard for the person on the other end if you can't spell right. If the person on the other end is me then you're not instilling a whole lot of confidence in the quality and professionalism of your work either.

If the person on the other end of your typo'd code is you (and it probably will be) then why don't you love yourself enough? You need a hug.

If I wrote the typo or awkward grammar, Word was broken and my internet's tubes were completely full of kittens that day so you have to forgive me (and not point it out). After all, I'm just a developer. You must understand - we write in code, not English.

Rethinking FizzBuzz - Function Z

The notion that a program as simple as FizzBuzz was over the heads of a non-trivial number of developer candidates caused a stir for the sort that reads programmer blogs.
I was as astounded as everyone else was - here's a program that's a tiny step above HELLO WORLD in terms of its complexity and one can actually use it to weed people who call themselves developers (and have the experience on their resumes to back it up) out? Really? But at the same time, guy's got a point - why don't we have prospective developers prove that they can really write code?
In the mad dash to whip it out and measure their programming prowess, people missed out on pondering another fundamental need from candidates - reading code.
Nobody wants to cop to being a lowly maintenance programmer. The digs aren't hard to find - "cut and paste programmer", "they just cobbled the code together", "Java is the new COBOL", etc.
But when you get down to it, how much of your code is really "new"? Do you refactor relentlessly (or ever)? Have you ever found a bug in your own code and fixed it? Have requirements changed, leaving you to modify your classes to get things working as they should today?
Congratulations, you're a maintenance programmer. As a maintenance programmer, you had to do something that I imagine manages to elude even more so-called developers than writing FizzBuzz - reading (and more importantly, understanding) code.
My tweak on the FizzBuzz script is the following snippet. Along with it, I pose the (very hopefully hypothetical) question - "I found this function in the code base the other day and 'z' obviously isn't very meaningful. Could you walk me through how you'd go about figuring out what to rename it?"

public static void z(int[] x)
{
for(int i = x.Length - 1; i > 0; i--)
{
for(int j = 0; j < i; j++)
{
if(x[j] > x[j + 1])
{
int t = x[j];
x[j] = x[j + 1];
x[j+ 1] = t;
}
}
}
}


To get it out of the way, yes, it's a bubble sort written in a C-like language. At this point, let me just state for the record, I think you're all better than me.</Seinfeld>
So why is this so much better than having someone write FizzBuzz? Think about it - if they can't write FizzBuzz, they can't read it either. If they blow either question, interview over. If they get FizzBuzz right, where do you go from there? It's a binary question (only right or wrong) and those aren't useful for interviews where you're trying to get a feel for the person.
With Function Z, you're weeding out the FizzBuzz failures (and more of them), plus you're getting some lightweight insight into their thought processes, plus bonus insights once they've figured out that it's a sort function.
For example...
  • Are they the prosaic type, questioning whether or not the language provided a native Sort() method on the integer array and why it wasn't used?
  • Are they a Not Invented Here cowpoke, quick to point out that you should rewrite it as a quick sort and speed things up (I can see how this feels like a trick question for an interview, but I get nervous about people who make changes with that little context (on the other hand, encapsulation and wow I wouldn't even hire myself, would I?))?
  • Are they the type that wants to leave everything better than they found it, cleaning up the variable names as they go along?
  • Do they ask for the unit tests to accompany it?!?!??!?


I can see how either FizzBuzz or Function Z can spin into the ugly territory of "monstrous interview question I ask to make them feel like a dick", so I implore you to keep your hypothetical functions short and sweet.
So who else has some deceptively simple interview techniques?

Sunday, June 3, 2007

Of bicycles and software

Thanks to getting hit last year by a guy in a pick-up truck who failed to yield, leaving me with a broken collarbone and multiple broken ribs, I've had a long absence from cycling. My old bike's still at the police station (maybe) and I've finally gotten a new one, which is a good thing - I've been crawling out of my skin to get back out on the roads and trails.
It's a gorgeous new bike. I'll stop short of calling it a work of art, but it sure is pretty. In just about every way I can quantify, it's better than my old bike. Yet despite this undeniable fact, I find myself not loving my bike and longing for my old bike, the one that I swore I'd never ride again.
It's piddly little things, like the derailleur levers being set up differently. The changes in frame geometry. Sticky rubber grips instead of hard rubber ones. All these seemingly inconsequential little things add up to me not being as in love with my new bike as I should be yet. I'm out on it and "did my old bike climb faster than this one?" and "is this new frame too rigid?" and other heresies run through my mind.

As I'm out riding, this brings to mind being down at the big corporate headquarters a while back, picking people's brains for this new application we're going to be building. They're showing us how they set up things on their end and things are, keeping it sophisticated, execrable.
The interface sometimes locks up, but you have to wait to see if it unlocks. If it doesn't, you have to close out because it probably means something's wrong and you'll lose your work. If you've gotten past this step, there's no going back and it means you have to re-do hours of keying because you can only export from System A to System B once. Hoops must be jumped through and chickens sacrificed just about every step of the way.
I see obvious little fixes that should be pretty simple and big time-savers and I run them by them They get uncomfortable with the propositions and the answers all basically boil down to "we can't do it your way because we do it our way." Of course, I scoff to myself all the way home because wow they're lifers but then again, the first thing I do with Windows XP is turn off the bells 'n whistles and get back to the Windows 2000 interface that I'm used to.

When it comes to interfaces, doing things right the first way obviously should be your first choice. Once it's been deployed and users have been banging away on it for years, take some time and reconsider overhauling the UI to use the newest and shiniest widgets you can find out there. There's a balance to be struck and you need to be smart about it.
No matter how god-awful the old UI was or how much pain it caused (and continues to cause), you're going to make users unhappy by changing things around. You think you're bringing joy into the world, but steel yourself for the onslaught of angry-grams when you release your update.
I look at pieces of the application and some parts are flat-out cringeworthy and I'd love to change them to hide my shame, but I have to ask myself - are these changes making me happy or are they making my users happy?

If you're sure you've made the right choice, stick with it. After all, some of your users are knuckle-draggers who do everything they can to make their new OS look like their old one and resent their bikes for little details that make damned near no difference and distrust applications that don't crash on them if they do something "wrong".
What do they know?