So, the time has come for the worst sin.
Just to recap - and so there is one post that lists them all - here are the ones that I've covered so far:
Some people have remarked that all of these are judgement calls, and really more a matter of aesthetics than actual sins.
That is true. I didn't include things like "naming your variables i, j, & k" as sins, because I don't think that's a real problem in most of the code I'm likely to have to deal with, and there really isn't much argument over whether it's a good idea or not.
It perhaps would have been better to title this series, "Seven things Eric would really prefer that you don't do in code that he has to work with", but that is both ungainly and lacking the vitality of a post with the term "sin" in it.
It's all marketing, you see - or you would if you were actually reading this post, but given my track record on the last six, it's probably a good idea to cut your losses now and spend your time more productively, like in switching your entire codebase from tabs to spaces (or spaces to tabs...)
When I was a kid, I was fairly interested in WWII. I read a lot of books about it, from general histories about the war, to books on the warfare in the Pacific, to books about the ground war in Europe.
One of the interesting features of the military during that time - one that I didn't appreciate until much later - was how they balanced the desire for advancement in their officer corps vs the need to only advance the most talented and capable. There were really two schools of thought at the time.
The first school advocated an approach where a lower-ranked officer - say, a colonel - would be promoted to fill a vacancy directly, on the theory that it made the chain of command cleaner, and you'd quickly find out if he had "the right stuff".
The second group advocated using "field promotions", in which a colonel would be temporarily promoted to see if he could perform in the job. The theory here was that the service would end up with only the best colonels promoted, and that it was much easier (and better for both the officer and the service) to let a field promotion expire rather than demote an officer already given a full promotion.
Over time, the approach advocated by the second group was borne out as having far better results, and the danger of the first approach was recognized.
Which brings us on our roundabout journey to our final sin:
Sin #1 - Premature Generalization
Last week I was debugging some code in a layout manager that we use. It originally came from another group, and is the kind of module that nobody wants to a) own or b) modify.
As I was looking through it, I was musing on why that was the case. Not to minimize the difficulty in creating a good layout manager (something I did a bit of in a previous life), but what this module does really isn't that complex, and it has some behavior that we would really like to change.
The problem is that there are at least three distinct layers in the layout manager. I write a line of code that says:
and when I step into it, I don't step into the appropriate TableFrame. I step into a wrapper class, which forwards the call onto another class, which forward onto another class, which finally does something.
Unfortunately, the relation between the something that gets done and the TableFrame class isn't readily apparent, because of the multiple layers of indirection.
Layers of indirection that, as far as I can tell (and remember that nobody wants to become the owner of this code by showing an any interest in it or, god forbid, actually making a modification to it...), aren't used by the way we use the layout manager. They're just mucking things up...
Why is this the #1 sin?
Well, as I've been going through the sins, I've been musing on how I ranked them. One of the primary factors that I used is the permanence of the sin.
And this one is pretty permanent. Once something is generalized, it's pretty rare that it ever gets de-generalized, and I this case, I think it would be very difficult to do so.
This might be slightly different if there were full method-level tests for the component - one could consider pulling out that layer. But even with that, it would be hard to approach in a stepwise fashion - it could easily turn into one of those 3-hour refactorings that makes you grateful that your source code control system has a "revert" feature.
Or, to put it another, fairly obvious way:
Abstraction isn't free
In one sense this seems obvious - when you develop a component that is used by multiple clients, you have to spend a little extra effort on design and implementation, but then you sit back and reap the benefits.
Or do you?
It turns out that you only reap the benefits if your clients are okay with the generalized solution.
And there's a real tendency to say, "well, we already have the ListManager component, we can just extend it to deal with this situation".
I've know teams where this snowballed - they ended up with a "swiss army knife" component that was used in a lot of different scenarios. And like many components that do a lot, it was big, complex, and had a lot of hard-to-understand behavior. But developing it was an interesting technical challenge for the developers involved (read that as "fun and good for their careers"...)
The problem came when the team found that one operation took about 4 times as long as it should. But because of the generalized nature of the component doing the operation, there was no easy way to optimize it.
If the operation had been developed from scratch without using the "uber-component", there would have been several easy optimization approaches to take. But none of those would work on the generalized component, because you couldn't just implement an optimization in one scenario - it would have to work for all scenarios. You couldn't afford the dev cost to make it work everywhere, and in this case, even if you could, it would cause performance to regress in other scenarios.
(At this point, I'm going to have to have anybody thinking "make it an option" escorted out of this post by one our friendly ushers. How do you think it got so complex in the first place?)
At that point, you often have to think about abandoning the code and redeveloping in the next version. And in the next cycle, this group *did* learn from their mistakes - instead of the uber-component, they built a small and simple library that different scenarios could use effectively. And it turned out that, overall, they wrote less code than before.
HaHA. I make joke.
What they really did was build *another* uber-component that looked really promising early (they always do), but ultimately was more complex than the last version and traded a new set of problems for the old ones. But, building it was a technical challenge for the developers involved, and that's what's really important...
How do you avoid this sin?
Well, YAGNI is one obvious treatment, but I think a real treatment involves taking a larger view of the lifecycle costs of abstraction and componentization.
It that one general component really going to be better than two custom solutions?
(if you didn't understand the story, look here and and see what rank is above a colonel...)
At the beginning of the sin-tacular, I asked for people to come up with their own lists. And here they are:
My original plan was to comment on some of the individual sins that people listed, but they're all great - you should go and read them all.
I was a bit intrigued, however, by Chris' comment (or should that be "Chris' Comments' comment?):
Hey, Eric, what are the 7 Heavenly Virtues of Programmers?
Yesterday I rode the Summits of Bothell. Like its more popular cousin to the south, S (I'm not a big fan of "cute" acronyms so I'm not going to use it...) is a ride focused around hills, originally 7 but now 8 in a bid to outdo the competition.
Their other bid to outdo the competition is in steepness. The advertisement sheet says two very troubling things. The first is an offhand comment about equipment:
Triple cranks and <24" low gear highly recommended as well as beefy brakes
24" refers to "gear inches", a measure of how far your bike moves forward for each pedal revolution. An average "riding on the flat" gearing for me is something like a 42-17 (teeth on the front/teeth on the rear), which is about 67 gear inches. My lowest gear (with my 12-27 rear cassette), is 30 gear inches. 24 gear inches is very low.
The second comment is a list of the hills:
Ascents - 14% (2), 16% (3), 18% (1).
Though I run a site for bicycle climbs, I am not a climbing specialist. But I have done some steep stuff. 10% is steep. 15% is painfully steep, and above that is just crazy.
But I've survived the Zoo multiple times, so I know what it takes to get up those kinds of hills. And I'm doing the Mountain 100 km Populaire in September, which includes the zoo (and other such climbs), so I need the practice at the steeps.
In other words, my strategy is sound.
My tactical decisions, however, are fairly suspect. I'm riding with a few friends, but what I've forgotten is that rides like this are self-selecting - the people that show up are the ones that can *climb*. I have at least 25 pounds on all of these guys, and at 170 pounds on my 6'2" frame, I'm not carrying a lot of weight.
Franklin doesn't ride that much more than me, but is strong on the flats and fast up the hills. He "went on a long run" yesterday.
Steve is riding his steel Colnago. Steve is scary fit - on our group rides, he'll just be talking to you at the bottom of a climb near the back of the group, and then he'll just ride past everybody on the ascent. He "rode 120 miles" yesterday. That is not a good sign, it's a sign that I'm out of my league. Anybody who rides 120 miles on Saturday and then shows up for a pain-fest on Sunday is to be watched.
And finally, we have Joe. Joe has focused in on the ride guarantee - "If not completely satisfied, you can ride the course a second time for FREE!"
He is planning on taking them up on that.
The start is very low key. A sign-in table, where you get a map (this is the 2005 map, and doesn't show the 8th hill up Hollyhills drive) and a T-shirt (included in the $25 fee). Some water and sports drink, and that's about it. And there are only about 20 people milling around. A nice change from the 3000 people that do flying wheels and the 9000 that do STP.
We head out to the first hill (Hollyhills), and ride up it. I start slow at the bottom, but stay about 10 seconds behind the group. It's fairly easy - 7%-8%, and I can compete on those grades. The group crests the top, circles once as I crest, and we descend down. One down.
The second hill (Bloomberg) is a different story. It starts at around 10%, the group gaps me, and then it kicks up to about 13%, and the gap grows. And then it gets steep - 16% (ish) for the last half block.
At this point, I part ways with the group. This is a very good thing - I killed myself on the southern cousin by trying to stay with this same group.
The next three hills - Beckstrom, Nike, and Westhill - blend together. They're steep, I ride up them trying to keep my HR in the low 150s. The day is perfect, and I talk to the very few riders I run into as we ride between hills.
Which takes us to Finn Hill. I've climbed this hill a few times, but never in this direction. This hills is probably the steepest one of the ride. I ride slow (around 4MPH), but manage to get up it in one piece without having to tack back and forth across the hill. This is the only place I see people walking.
Norway hill is next, but it's Norway from the South side, the easy way up, then Brickyard Road, and back to the finish, my finish-line ice cream, and my Costco cookie.
I don't have my full stats, but I do know my average speed was 13.4 MPH. There are no real flats on this ride, and there are a lot of stop signs and traffic lights, so don't expect to be pace-lining it.
Discounting the considerable amount of pain involved in the climbs, this was a very enjoyable ride - I liked it more than its smaller cousin. Very low key - the rest stops were very tiny but with great volunteers, cold water and the right food. And they had some cookies at the end of the ride, when you really want something solid. The course markings were all great.
I do have one small complaint with the signage. When I'm climbing, it's nice to know how to mashall my effort, and to do that I need to know when the pain will end. On several of the climbs, you'll finish the really steep section, and then ride at least half a mile on gentle slopes or rolling terrain to get to the "summit". Delaying the signage until that point diminishes the feeling of accomplishment at finishing the steep section, and makes you think there's more steep. I think Norway is the only exception, where the climb finishes right at the top.
It also makes the stats seem weird. Bloomberg hill is 440' high at the true summit, but the climb distance is perhaps 7000 feet, giving it a gradient of only about 6%. But the section on 240th gains 230 feet in 1770 feet, putting it right at 13%, and that's the average for that section, not the max.
It would be nice to have an indication of the steeps on the map (perhaps with some beads of sweat on the route), and a sign that says, "Pain lessens" at the end of each steep section.
I was going to suggest that they get a real website, but that would encourage more riders to participate...
Raymond wrote a really nice series of posts on this:
He also points out that you might want to read Grant's anonymous method posts...
Dare wrote a post talking about the advisability of making developers do operations.
Which is really part of a philosophical question...
When you're setting up a software organization, how much specialization should you have, and where you should you draw the lines around the responsibilities of the various groups?
Some orgs take a very generalized view of what people own, and others take a very specialized view. I've worked in both sorts of environments.
I've worked for a startup where, as a developer, I wrote the code, tested the code, built the code, made tapes to ship out to customers, and answered customer support calls.
And I've worked in other organizations where the job of developer was to implement what was written down in the spec and pass it off to the QA org. Those orgs typically had structures and policies designed to insulate the developers, so they wouldn't be distracted.
That eliminated a bunch of the outside noise that they would otherwise have to deal with, and make them more efficient at getting their development work done.
And how did those efficient organizations fare in their products?
Not very well.
They were reasonably good at shipping software, but their software didn't turn out to be very good for users. New updates didn't address issues that users had been hitting. New features were hard to use and/or didn't hit the sweet spot. They answered questions that users didn't ask.
All of this was because the developers were out of touch with people who had to deal with their software. They didn't feel the pain that the users were experiencing setting up their software. They didn't feel the pain when a bug in the software meant that the user's business was loosing money. And they didn't understand why users were having trouble using features that seemed obvious to them.
All that happened in DevDiv, and the issues showed up in our customer satisfaction numbers. So, it was decided to let developers (and the testers, and PMs...) talk directly with customers.
There was a fair amount of angst around this decision. It would take up too much dev time. Developers would insult customers. Customers didn't know enough to give good feedback.
But it turned out that all of those things were wrong. The developers liked to solve problems, and they also liked to help people. They remotely debugged customer issues on other continents. And they listened very closely to the detailed feedback customers gave about how the current software didn't meet business needs and what was good and bad about future plans.
And the organization adapted what they were planning, so that it addressed the areas that needed addressing.
Distraction is not the enemy. Pain is not the enemy. Pain is to be embraced, because only through feeling pain are you motivated to make it go away.
Raymond brings up an update on the monorail collision in Seattle.
I started adding a comment to his post, but why bore a few people who read his comments when I could bore more people if I wrote my own post...
As many Seattleites know, the monorail was built for the 1962 worlds fair, with a vision of of the future, including advanced transportation. At that time, the one-mile (ish) monorail was one of the attractions of the fair (the other being the space needle with it's revolving restaurant, renowned for it's view (and *nothing else*...)). It may also have served to bring people to the fair site (now Seattle Center) from the downtown hotels, assuming there were downtown hotels in 1962.
Visitors loved the monorail, and locals were so smitten that the Seattle metropolitan area was soon home to an elegant elevated monorail system that whisked people from conveniently located centers to wherever they wanted to go in minutes.
That's sort of a Seattle joke. Seattle has this civic inability to actually start building any rational project. We instead worry and study (to the tune of millions of $$$) whether we can afford it and whether it's the right thing to do. Paul Allen fronted (anonymously) a project named "The Commons", which would have created a fabulous park at the south end of lake union (Seattle really doesn't have any big city parks), and he put in $20 million of his own money (in return for matching city money). Seattle voters wouldn't go for it. More recently, Seattle voters approved a new monorail, which was very nice and beautiful but got cancelled because it would a) cost more than the estimates and b) be insufficiently aesthetically pleasing.
As an aside, I did notice elevated platforms for the Sound Transit line that will run up to SeaTac recently, so at least somebody is making progress, but note that it's not *Seattle*.
So, the original monorail was largely a tourist attraction but not really useful in any way, leading a local folk group to write song entitled, "Monorail - train to nowhere".
Then, in the late 1980s, the Central Association came up with a plan to redevelop a block downtown as "Westlake Center", and proposed that the monorail be extended to end inside the shopping center.
Which made a lot of sense, but the geometry of the situation meant that the practical way of doing it required placings tracks closer together, setting the stage for the 2005 collision.
Repairing the monorail afterwards required new doors to be custom made, and in an interesting bit of civic assistance, they were built by the props department of the Seattle Opera.
Carmichael Training Systems "Classic" Review
(sorry for the duplicate post. I started this a few weeks ago, so it was a few posts back and didn't appear at the top of the web page as I had planned.)
My good friend Dan writes a post about why Visual Studio shouldn't be part of Windows, in response to a post by Jeroen.
I agree with all the reasons that Dan lists, and would like to add two of my own.
The first is that vista is already big enough. Strike that, Vista is already 8x bigger than it should be, from an agility standpoint. Adding in another application only makes that worse.
But there's a more important reason.
I'm part of a team that creates Windows Movie Maker, a very nice free downloadable video editor that runs on Windows XP. We have the same sort of discoverability problem that Visual Studio Express has - people don't know that Microsoft creates such a product, so they don't know that they can go and download it.
Well, actually, that's not quite true. It is true that the vast majority of people don't know there is a free product known as Movie Maker. It is false that they don't know that they can download it, because Movie Maker is part of SP2.
Discoverability is not about whether something is on your machine or not. It's whether the information is available to you through a source that you will pay attention to.
The 16-year old kid that might turn into a great programmer won't find VS through spelunking his Windows installation. He won't find it through a menu pick.
He'll find it through the message he gets from his computer instructor, through the chat message from his gamer pal, or through the post on BoingBoing or Digg. Or maybe through the splash screen on a game that he downloads...
Yesterday was my wife's birthday.
There were two presents of note. The first was a serotonin necklace from Made With Molecules. If you have a significant other who has a science-related job, this is a nice gift. Assuming he/she likes jewelry.
The second was her own google ad. I bought adwords on her name, and then wrote an ad that said, "Happy Birthday Kim" that linked to a page with a simple message on it.
And then I told her that she should search for information on herself.
The idea came from a post that Ray did a while back. You may note that I am also for sale on Amazon. If you want to do this you need to:
And as a note, the original post didn't have to do with Google recruiting. IIRC, it's a hobby of somebody Ray knows...
Here's an easy-to-make dinner salad, though it does involve a fair amount of chopping...
Defrost chicken breast in Microwave. Cut into 1/2" chunks. Salt liberally on all sides. Set aside.
Heat medium frying pan, and cook prosciutto until browned spots appear on each side. Drain on paper towels, and chop into 1/4" square pieces. Put in a large bowl.
Dice pepper, muschrooms, tomatoes, onions, pea pods and olives into small pieces, and add to bowl. Put garbanzos on cutting board and chop/crush them a bit, then add them to the bowl.
Finely crumble the feta cheese and add to the bowl. Add lemon juice, a splash of olive oil, a bit of freshly ground pepper, and mix well.
Heat non-stick frying pan until it's hot, and add chicken pieces. Spread them out so that they are all in contact with the heat, but move them as little as possible. Let them cook for 2-3 minutes, then flip them over, turning pieces so the raw parts are in contact with the heat. Cook a further 2-3 minutes, and then chop a few pieces open to see if they're done. Cook until all are done.
Add cooked chicken to vegatable mixture, and mix well. Taste and adjust seasoning as necessary.
The Morale Marauder (carpe factum)
(via The Mindset (via Chris' Comments)