2015-04-12

2013 WRX Swaybar Installation

Introduction


My wife and I run a 2013 Subaru WRX hatchback at autocross and rallycross events. In an effort to improve handling and reduce tire wear, we decided to upgrade the front and rear swaybars and endlinks. We chose a 24mm two-position adjustable front swaybar and a 22mm three-position adjustable rear swaybar and adjustable front and rear endlinks, all from Whiteline. The rear swaybar comes with an additional support brace to provide a little added stiffness.

The equipment came from the good folks at Subie Autosport, who were more than happy to advise us on what would work best for our purpose. Two days after we ordered, the gear was on our doorstep.

This is the story of two people with limited mechanical ability attempting to do this installation. I have done a few oil changes and many wheel/tire swaps, but little else. My wife has done similar work on other cars but not the WRX yet.

I've read many forum posts on the subject, I've watched about a dozen videos, and I've read the included directions. My wife also read the directions and watched a couple of the install videos with me. Supposedly this is under an hour of work per swaybar; we gave ourselves the better part of a day to do it, given our collective inexperience.

Part I: Front Swaybar & Endlinks


Step 1: Remove the plastic undertray. This went smoothly.

Step 2: Remove the crossmember to gain access to the swaybar. This is where the trouble started. Ten bolts, all stuck like they were glued in. Doused everything in penetrating oil, eventually got all the bolts off using a breaker bar, which we had to run out and buy (I didn't own one). I think this is where I injured my shoulder - seems like a partially torn rotator cuff. Getting the crossmember off took about three hours, including giving the oil some time to sink in, and going to the auto parts store for the breaker bar.

Step 3: Remove the swaybar. Again, every bolt was seized. Doused them all with penetrating oil, let it sit. Got the nuts off the endlinks. Got the nuts off the D-brackets. Removing the bolts from the D-brackets, one of the bolts sheared. We tried for a while to remove the end bolt from the top but access is extremely poor. Did some research, and decided to try using an easy-out in the hope we wouldn't have to drill out and re-ream the bolt hole. We finally got the brackets off, disconnected the endlinks, and got the swaybar off. At this point it was 10pm, about five hours in to our two-hour project, and we don't own an easy-out. I loaded up on NSAIDs and iced my shoulder.

The next day, went to the hardware store and picked up an easy-out, and after a lot of drilling, finally got the bolt out. At this point my wife is doing most of the work, I'm mostly providing technical advice and the occasional third hand. Despite the package's claim of "10 seconds", this took us about an hour and the better part of an 18v drill battery. We also went to the auto parts store and got a replacement bolt.

Step 4: Remove the endlinks. Surprise, these bolts were stuck too. Penetrating oil and a lot of elbow grease finally got them off. It is incredibly difficult to hold an allen wrench while you wrench a nut off, with your hand inside the wheel.

Step 5: Install the new endlinks. The Whiteline endlinks come with nuts that have a plastic piece on the front side, which you usually see in parts where they use it as a stop so you can't over-tighten the nut. In this case, however, you're supposed to torque past it, which we found after some more research. Also, those red plastic things that are in every product picture? For protection during shipping. Remove them before installing - not mentioned in the instructions.

We get them torqued down and run out of depth in the socket. My box-end wrench set only goes up to 14mm and the nuts on the Whiteline endlinks are 17mm. It's Sunday, and the shops are closed by this time. We attach both as best we can and leave it until Monday so we can run out yet again for more tools.

Monday we get a 17mm deep socket (no 17mm box-ends to be found), and get the endlinks attached. Day 3, after 5 trips for parts and tools, we've finally got new parts installed on the car.

Step 6: Install the new swaybar. Once the endlinks were on, getting the swaybar on and the first endlink attached was relatively short work. We put them on the softer of the two settings. The new bushings are grease-less, so there really wasn't much to it.

Getting the second endlink attached turned out to be an ordeal. It seemed physically impossible for the endlink to fit. We did some research online and found some people were attaching the endlink to the swaybar first, then to the chassis, so we attempted that, which worked after some finagling.

We found a service manual online for the torque specs, and torqued everything down... except the torque wrench we have is too large to use on the endlinks, so yet another trip to the auto parts store had us a new torque wrench for smaller spaces.

Step 7: Reinstall the crossmember. Just a matter of elbow grease.

Finishing up: At last, we got everything torqued down and took it for a test drive - we were going to do the front and rear at once, but at this point we were three days in and wanted to see some results. Turn-in is much improved, and the reaction to steering input is much quicker, making all that work worthwhile. Re-torqued everything after the test drive, per the instructions.

Part II: Rear Swaybar, Endlinks and Braces


We did the rear the following weekend, so my shoulder had a chance to heal and I was back to doing my fair share. The rear went far more smoothly than the front, but we did run into some small snags.

Step 1: Remove the factory swaybar. This wasn't terribly difficult, it just required some muscle and a breaker bar.

Step 2: Remove the factory endlinks. This was a little bit harder; the lower connection point in the rear crossmember was pretty well seized. A good deal of penetrating oil and muscle on the breaker bar finally got them free.

Step 3: Install the new endlinks. Because of they way they slot into the crossmember, which is a tight fit, this took some finagling; wiggling them back and forth and applying pressure got them into the proper position. We greased the new bolts and hand-tightened them, so they could still be moved side to side to make it easier to get them into the swaybar.

Step 4: Install the new swaybar. Again, greaseless bushings make this a pretty streaightforward task... until we stripped the upper bolts for the D-brackets that attach the bushings. Both of them. I'm not sure what the issue was, but both sides did the same thing - tighten, tighten, fine, then as we tried to torque them down, just as it seemed to be getting close to the proper torque setting, they suddenly got easier to turn again - and kept turning. Not spinning free like it was totally stripped, they just never got tighter.

Since the rear of the bolt is accessible, we picked up a couple of M8 bolts, put them on the back, and torqued it down to spec without issue. Then we got the endlinks attached, putting the rear on its middle setting, and torqued those down. There was also an issue with the lower bolt on the driver's side, which was inaccessible with a socket because the exhaust is in the way; this hand to be hand-torqued. We basically just torqued the bolt on the passenger side with the torque wrench, and then torqued the driver's side with a box-end wrench so it felt the same. Certainly not perfect, but the best we had available.

Step 5: Install the braces. The most difficult part was getting the lower control arm bolt off in order to attach the braces; these seemed to be torqued far tighter than the factory-specified 59 ft-lbs. It was tight enough that we couldn't do it with a breaker bar using a 1/2"-to-3/8"-drive adapter, and had to run out for a 1/2"-drive socket set. A lot of penetrating oil, patience, and torque finally got them off. We attached the braces, which connect the lower attachment point of the D-bracket to the inner attachment point of the control arm.

Finishing up: We torqued everything down and took it for a test drive; all good. The steering is solid and stable, roll is vastly reduced, turn-in is better, steering is significantly more responsive, and much more neutral, with just a hint of oversteer. We lift it back up and torque everything down again per instructions.

A week later, we torqued it all down one last time; you're supposed to do it after the first 100mi but we didn't have a chance until the next weekend with almost three times that. Most of the bolts were still set, a couple were off just a little.

Conclusion

All in all it took us much longer than it probably should have, certainly longer than we thought it would have, and required all sorts of equipment we didn't know we'd need. But the car is better for it, and we get the pride of knowing not only did we install it ourselves, but we were able to navigate a few difficulties along the way and surpass them to get the job done. Not to mention that our garage is a bit better equipped - we spent $5 on nuts and bolts and all the rest was on tools that will last and we'll use again.

We both learned a lot doing it - my wife commented on how far she had come just in terms of knowing the tools. At the start I had to occasionally explain here and there what some tool was called or what the differences between them were, but by the end she knew them all and had no trouble choosing the right socket set for the job out of what was available at the store when we founded we needed something we didn't have.

And you know what? Working on a car with your wife is pretty nice. It might have gone more smoothly and quickly if there was someone there who knew what they were doing, rather than two complete amateurs. But it was a shared experience, it was time spent together, and I can't imagine anyone I'd rather crawl around under a ton and a half of Japanese engineering with.

2014-03-21

A Wrinkle in Time

You've built a prototype, everything is going great. All your dates and times look great, they load and store correctly, everything is spiffy. You have your buddy give it a whirl, and it works great for them too. Then you have a friend in CuraƧao test it, and they complain that all the times are wrong - time zones strike again!

But, you've got this covered. You just add an offset to every stored date/time, so you know the origin time zone, and then you get the user's time zone, and voila! You can correct for time zones! Everything is going great, summer turns to fall, the leaves change, the clocks change, and it all falls apart again. Now you're storing dates in various time zones, without DST information, you're adjusting them to the user's time zone, trying to account for DST, trying to find a spot here or there where you forgot to account for offsets...

Don't fall into this trap. UTC is always the answer. It is effectively time-zone-less, as it has an offset of zero and does not observe daylight savings time. It's reliable, it's universal, it's always there when you need it, and you can always convert it to any time you need. Storing a date/time with time zone information is like telling someone your age by giving your birthday and today's date - you're dealing with additional data and additional processing with zero benefit.

When starting a project, you're going to be better off storing all dates as UTC from the get-go; it'll save you innumerable headaches later on. I think it is atrocious that .NET defaults to system-local time for dates; one of the few areas where I think Java has a clearly better design. .NET's date handling in general is a mess, but simply defaulting to local time when you call DateTime.Now encourages developers to exercise bad practices; the exact opposite of the stated goals of the platform, which is to make sure that the easy thing and the correct thing are, in fact, the same thing.

On a vaguely related note, I've found a (in my opinion) rather elegant solution for providing localized date/time data on a website, and it's all wrapped up in a tiny Gist for your use: https://gist.github.com/aprice/7846212

This simple jQuery script goes through elements with a data attribute providing a timestamp in UTC, and replaces the contents (which can be the formatted date in UTC, as a placeholder) with the date/time information in the user's local time zone and localized date/time format. You don't have to ask the user their time zone or date format.

Unfortunately it looks like most browsers don't take into account customized date/time formatting settings; for example, on my computer, I have the date format as yyyy-mm-dd, but Chrome still renders the standard US format of mm/dd/YYYY. However, I think this is a relatively small downside, especially considering that getting around this requires allowing users to customize the date format, complete with UI and storage mechanism for doing so.

2014-03-13

On Code Comments

I've been seeing a lot of posts lately on code comments; it's a debate that's raged on for ages and will continue to do so, but for some reason it's been popping up in my feeds more than usual the last few days. What I find odd is that all of the posts generally take on the same basic format: "on the gradient of too many to too few comments, you should aim for this balance, in this way, don't use this type of comments, make your code self-documenting." The reasoning is fairly consistent as well: comments get stale, or don't add value, or may lead developers astray if they don't accurately represent the code.

And therein lies the rub: they shouldn't be representing the code at all. Code - clean, self-documenting code - represents itself. It doesn't need a plain-text representative to speak on its behalf unless it's poorly written in the first place.

It may sound like I'm simply suggesting aiming for the "fewer comments" end of the spectrum, but I'm not; there's still an entity that may occasionally need representation in plain text: the developer. Comments are an excellent way to describe intent, which just so happens to take a lot longer to go stale, and is often the missing piece of the puzzle when trying to grok some obscure or obtuse section of code. The code is the content; the comments are the author's footnotes, the director's commentary.

Well-written code doesn't need comments to say what it's doing - which is just as well since, as so many others have pointed out, those comments are highly likely to wind up out-of-sync with what the code is actually doing. However, sometimes - not always, maybe even not often, but sometimes - code needs comments to explain why it's doing whatever it's doing. Sure, you're incrementing Frobulator.Foo, and everybody is familiar with the Frobulator and everybody knows why Foo is important and anyone looking at the code can plainly see you're trying to increment it. But why are you incrementing it? Why are you incrementing it the way you're doing it in this case? What is the intent, separate from its execution? That's where comments can provide value.

As a side note (no pun intended), I hope we can all agree that doc comments are a separate beast entirely here. Doc comments provide meta data that can be used by source code analyzers, prediction/suggestion/auto-completion engines, API documentation generators, and the like; they provide value through some technical mechanism and are generally intended for reading somewhere else, not for reading them in the source code itself. Because of this I consider doc comments to be a completely separate entity, that just happen to be encoded in comment syntax.

My feelings on doc comments are mixed; generally speaking, I think they're an excellent tool and should be widely used to document any public API. However, there are few things in the world more frustrating that looking up the documentation for a method you don't understand, only to find that the doc comments are there but blank (probably generated or templated), or are there but so out of date that they're missing parameters or the types are wrong. This is the kind of thing that can have developers flipping desks at two in the morning when they're trying to get something done.

2014-02-25

New Laptop! ASUS ROG G750JW

I recently received the generous gift of an ASUS ROG (Republic of Gamers) G750JW laptop, and let me tell you, the thing is a beast. Seriously, it's huge.

It's a 17" widescreen laptop (1920x1080 TN panel, no touch thankyouverymuch), with an extra two inches or so of chassis behind the hinge. It also weighs just short of ten pounds.

But, I wasn't looking for an ultraportable. I wanted something that I could use around the house and on the road, primarily for software development, but also for occasional gaming. That meant I needed a comfortably-sized keyboard, trackpad, and display; that meant a 17" laptop. I wanted decent battery life and decent performance, which meant it would be heavy for its size. And I got exactly what I asked for.

The G750JW runs a Core i7 at 3.2GHz, 12GB of RAM, an NVidia GeForce 765m, and a 750GB HDD. Step one was replacing the HDD with a 240GB Crucial M500 SSD I picked up for $135 on Amazon - less than half what I paid for a nearly identical drive just over a year ago. The difference in speed is truly staggering, going from a 5400 RPM laptop hard drive to a full-tilt SSD. It also cut a few ounces off the weight, and added a good half hour to hour of working time on the battery, so a win across the board.

I tried installing Windows 7 on it as I despise Windows 8, but kept running into an error during the "extracting files" stage of the installation. I found numerous posts online from people with the same problem, some of them with solutions, but none of those solutions worked for me; from what I can tell, it appears to be some conflict between the latest-and-greatest UEFI in the G750's motherboard and the aging Windows 7 OS. It's a shame, but I suppose being forced to gain more familiarity with Windows 8 isn't all bad; I just wish I had the option to use something more, well... usable.

Other than the OS though, it's been a joy. It performs extremely well, it has all the features and specs I need for what I'm using it for, and it's a beast for gaming - more horsepower than I really need considering I'm not a huge gamer and gaming was not the primary purpose of the laptop to begin with. Part of its bulk comes from the two huge rear-venting fans in the thing, which do a good job of keeping it cool - something I've had problems with when using other laptops, and which was the ultimate bane of my wife's old MacBook Air. I don't think I need to worry about it overheating and locking up while playing video like the MBA did on a regular basis.

My only gripe at the moment is that it seems to be impossible to find a decent Bluetooth mouse. Sure, the market is flooded with wireless laptop mice; but 95% of them use a proprietary receiver (I'm looking at you, Logitech!) rather than native Bluetooth, which requires you to use the provided USB dongle. That seems like an utter waste considering the laptop has a built-in transceiver capable of handling mice without any USB dongle.

All I really want is a decent-sized (I have large hands) Bluetooth wireless mouse, with a clickable scroll wheel and back/forward thumb buttons. That doesn't seem like too much to ask, but as far as I can tell, it just doesn't exist. Thankfully the laptop has a very generous touchpad with multi-touch, and clicking both the left and right buttons together generates a middle-click. Still, I really hope Logitech gives up on the proprietary wireless idea and gets on board with the Bluetooth standard, because I'd like to have a decent mouse to use with it.

It's telling that, on Amazon, you can find a discontinued Logitech Bluetooth mouse that meets my requirements - selling in new condition for a mere three hundred dollars. That's three times what Logitech's finest current proprietary wireless mouse costs, for an outdated, basic mouse. That's how much standard Bluetooth wireless is worth to people. Wake up Logitech!

Any suggestions on a suitable mouse in the comments would be greatly appreciated...

2014-02-07

Optimizing Entity Framework Using View-Backed Entities

I was profiling a Web application built on Entity Framework 6 and MVC 5, using the excellent Glimpse. I found that a page with three lists of five entities each was causing over a hundred query executions, eventually loading a huge object graph with hundreds of entities. I could eliminate the round trips using Include(), but that still left me loading way too much data when all I needed was aggregate/summary data.

The problem was that the aggregates I needed were complex and involved calculated properties, some of which were based on aggregates of navigation collection properties: a parent had sums of its children's properties, which in turn had sums of their children's properties, and in some cases parents had properties that were calculated partly based on aggregates of children's properties. You can see how this quickly spun out of control.

My requirements were that the solution had to perform better, at returning the same data, while allowing me to use standard entity framework, code first, with migrations. My solution was to calculate this data on the server side, using entities backed by views that did the joining, grouping, and aggregation. I also found a neat trick for backward-compatible View releases:

IF NOT EXISTS (SELECT Table_Name FROM INFORMATION_SCHEMA.VIEWS WHERE Table_Name = 'MyView')
    EXEC sp_executesql N'create view [dbo].[MyView] as select test = 1'
GO
ALTER VIEW [dbo].[MyView] AS
SELECT ...

It's effectively upsert for views - it's safe to run whether or not the view already exists, doesn't ever drop the view if it does exist (leaving no period where a missing view might cause an error), and it doesn't require keeping separate create and alter scripts in sync when changes are made.

I then created the entities that would represent the views, using unit tests to ensure that the properties now calculated on the server matched expected values the same way that the original, app-calculated properties did. Creating entities backed by views is fairly straightforward; they behave just like tables, but obviously can't be modified - I made the property setters protected to enforce this at compile time. Because my View includes an entry for every "real" entity, any query against the entity type can be cast to the View-backed type and it will pull full statistics (there is no possibility of an entity existing in the base table but not in the view).

Next I had to create a one to one association between the now bare entity type and the view type holding the aggregate statistics. The only ID I had for the view was the ID of the raw entity it was connected to. This turned out to be easier said than done - entity framework expects that, in a one to one relationship, it will be managing the ID at one end of the relationship; in my case, the ID's at both ends were DB-generated, even though they were guaranteed to match (since the ID in the view was pulled directly from the ID in the entity table).

I ended up abandoning the one-to-one mapping idea after a couple days' struggle, instead opting to map the statistics objects as subclasses of the real types in a table per type structure. This wound up being relatively easy to accomplish - I added a table attribute to the sub type, giving the name of the view, and it was off to the races. I went through updating references to the statistics throughout LINQ queries, views, and unit tests. The unit and integration tests proved very helpful in validating the output of the views and offering confidence in the changes.

I then ran my benchmarks again and found that pages that had required over a hundred queries to generate now used only ten to twenty, and were rendering in half to a third the time - a one to two hundred percent improvement, using views designed purely to mimic the existing functionality - I hadn't even gone about optimizing them for performance yet!

After benchmarking, it looks even better (times are in milliseconds, min/avg/max):

EF + LINQEF + Views
3 lists of 5 entities (3 types)360/785/167560/105/675
2 lists of 6 entities (1 type)325/790/193590/140/740
1 entity's details + 1 list of 50 entities465/975/268590/140/650

These tests were conducted by running Apache JMeter on my own machine against the application running on Windows Azure, across a sampling of 500 requests per page per run. That's a phenomenal 450 to 650 percent improvement across the board on the most intensive pages in the application, and has them all responding to 100% of requests in under 1 second. The performance gap will only widen as data sets grow; using views will make the scaling much more linear.

I'm very pleased with the performance improvement I've gotten. Calculating fields on the app side works for prototyping, but it just can't meet the efficiency requirements of a production application. View-backed entities came to the rescue in a big way. Give it a try!

2014-02-06

You're Being Held Hostage and You May Not Even Know It

To me, net neutrality isn't about fair business practices between businesses. That's certainly part of it, but it's not the crux of the issue. To me, net neutrality is about consumer protection.

Your broadband provider would like to charge companies - particularly content companies - extra in order to bring you their content. Setting aside the utterly delirious reasoning behind this for the moment, let's think about this from the consumer's perspective. You're paying your ISP to provide you access to the internet - the whole thing. When you sign up for service, you're signing up for just that: access to the internet. Period. What your ISP fails to disclose, at least in any useful detail, is how they intend to shape that access.

For your $40, $50, $60 or more each month, you might get high-speed access to some things, and not to others. You don't get to know what ahead of time, or even after you sign up - the last thing your ISP wants is for you to be well-informed about your purchase in this regard. They'll do whatever they can to convince you that your service is plain, simple, high-speed access to the whole internet.

Then, in negotiations behind closed doors, they're using you as a hostage to extort money from the businesses you're a customer of. Take Netflix as an example: you pay your ISP for internet service. Netflix also has an ISP, or several, that they pay for internet service. Those ISPs have what are called "peering arrangements" that determine who, if anyone, pays, and how much, when traffic travels between their networks on behalf of their customers. This is part and parcel of what you and Netflix pay for your service. You pay Netflix a monthly fee to receive Netflix service, which you access using your ISP. Netflix uses some part of that monthly fee to pay for their own internet service.

Your ISP has gone to Netflix and said "hey, if you want to deliver high-definition video to your customers who are also my customers, you have to pay me extra, otherwise my customers which are also your customers will receive a sub-par experience, and they might cancel their Netflix account." They're using you as a bargaining chip without your knowledge or consent, in order to demand money they never earned to begin with; everyone involved is already paying their fair share for their connection to the global network, and for the interconnections between parts of that global network.

To me, when a company I do business with uses me, and degrades my experience of their product, without my knowledge or consent, that's fraud from a consumer standpoint. Whatever Netflix might think about the deal, whether Netflix is right or wrong in the matter, doesn't enter into it; I'm paying for broadband so that I can watch Netflix movies, I'm paying for Netflix so that I can watch movies over my broadband connection, and my ISP is going behind my back and threatening to make my experience worse if Netflix doesn't do what they want. Nobody asked me how I feel about it.

Of course, they could give full disclosure to their customers (though they never would), and it wouldn't matter a whole lot, because your options as a broadband consumer are extremely limited; in the majority of cases, the only viable solution is cable, and when there is competition, it comes from exactly one place: the phone company. The cable companies and phone companies are alike in their use of their customers as hostages in negotiations.

What about fiber broadband? It's a red herring - it's provided by the phone company anyway. Calling fiber competition is like saying Coke in cans competes with Coke in bottles - it's all Coke, and whichever one you buy, your money goes into Coke's pocket.

What about wireless? Wireless will never, ever be able to compete with wired service, due to simple physics. The bandwidth just isn't there, the spectrum isn't there, there's noise to contend with, and usage caps make wireless broadband a non-starter for many cases, especially streaming HD video. Besides, the majority of truly high-speed wireless service is provided by the phone companies anyway; see the previous paragraph.

Why aren't they regulated? The FCC is trying, in its own way, but there's little traction; the cable and telephone companies have the government in their collective pockets with millions of dollars of lobbying money, and We The People haven't convinced Our Government that we care enough for them to even consider turning down that money.

In the United States, we pay many, many times what people pay in much of the developed world, and we get many, many times less for what we spend. On top of that, our ISPs are using us as bargaining chips, threatening to make our already overpriced, underpowered service even worse if the companies we actually chose in a competitive market - unlike our ISPs - don't pay up. This is absolutely preposterous, it's bordering on consumer fraud, and you should be angry about it. You should be angry enough to write your congressman, your senator, the president, the FCC, and your ISP (not that the last will do you much good, but it can't hurt.)

Some excellent places to find more information:

2013-12-18

Engineers, Hours, Perks, and Pride

This started as a Google+ post about an article on getting top engineering talent and got way too long, so I'm posting here instead.

I wholeheartedly agree that 18-hour days are just not sustainable. It might work for a brand-new startup cranking out an initial release, understaffed and desperate to be first to market. But, at that stage, you can expect the kind of passion and dedication from a small team to put in those hours and give up their lives to build something new.

Once you've built it, though, the hours become an issue, and the playpen becomes a nuisance. You can't expect people to work 18-hour days forever, or even 12-hour days. People far smarter than I have posited that the most productive time an intellectual worker can put in on a regular basis is 4 to 6 hours per day; after that, productivity and effectiveness plummet, and it only gets worse the longer it goes on.

Foosball isn't a magical sigil protecting engineers from burn-out. Paintball with your coworkers isn't a substitute for drinks with your friends or a night in with your family. An in-house chef sounds great on paper, until you realize that the only reason they'd need to provide breakfast and dinner is if you're expected to be there basically every waking moment of your day.

Burn-out isn't the only concern, either. Engineering is both an art and a science, and like any art, it requires inspiration. Inspiration, in turn, requires experience. The same experience, day-in, day-out - interacting with the same people, in the same place, doing the same things - leaves one's mind stale, devoid of inspiration. Developers get tunnel-vision, and stop bringing new ideas to the table, because they have no source for them. Thinking outside the box isn't possible if you haven't been outside the box in months.

Give your people free coffee. Give them lunch. Give them great benefits. Pay them well. Treat them with dignity and respect. Let them go home and have lives. Let them get the most out of their day, both at work and at home. You'll keep people longer, and those people will be more productive while they're there. And you'll attract more mature engineers, who are more likely to stick around rather than hopping to the next hip startup as soon as the mood strikes them.

There's a certain pride in being up until sunrise cranking out code. There's a certain macho attitude, a one-upmanship, a competition to see who worked the longest and got the least sleep and still came back the next morning. I worked from 8am until 4am yesterday, and I'm here, see how tough I am? It's the geek's equivalent to fitness nuts talking about their morning 10-mile run. The human ego balloons when given the opportunity to endure self-inflicted tortures.

But I'm inclined to prefer an engineer who takes pride in the output, not the struggle to achieve it. I want someone who is stoked that they achieved so much progress, and still left the office at four in the afternoon. Are they slackers, compared to the guy who stayed another twelve hours, glued to his desk? Not if the output is there. It's the product that matters, and if the product is good, and gets done in time, then I'd rather have the engineer that can get it done without killing themselves in the process.

"I did this really cool thing! I had to work late into the night, but caffeine is all I need to keep me going. I kept having to put in hacks and work-arounds, but the important thing is that it's done and it works. I'm a coding GOD!" Your typical young, proud engineer. They're proud of the battle, not the victory; they're proud of how difficult it was.

"I did this really cool thing! Because I had set myself up well with past code, it was a breeze. I was amazed how little time it took. I'm a coding GOD!" That's my kind of developer. That's pride I can agree with. They're proud because of how easy it was.

This might sound like an unfair comparison at first, but think about it. When you're on a 20-hour coding bender, you aren't writing your best code. You're frantically trying to write working code, because you're trying to get it done as fast as you can. Every cut corner, every hack, every workaround makes the next task take that much longer. Long hours breed technical debt, and technical debt slows development, and slow development demands longer hours. It's a vicious cycle that can be extremely difficult to escape, especially once it's been institutionalized and turned into a badge of honor.

2013-12-16

My Personal Project Workflow/Toolset

I do a lot of side projects, and my personal workflow and tooling is something that's constantly evolving. Right now, it looks something like this:

  • Prognosticator for tracking features/improvements, measuring the iceberg, and tracking progress
  • WorkFlowy for tracking non-development tasks (the most recent addition to the toolset)
  • Trac for project documentation, and theoretically for defect tracking, though I've not been good about entering defects in Trac recently; it doesn't seem worth the effort on a one-person project, though with multiple people I think it would be a must
  • Trello for cross-cutting all the above and indicating what's next/in progress/recently completed, and for quickly jotting down ideas/defects. Most of the defect tracking actually goes in here on one-man projects right now. This is a lot of duplication and the main source of waste in my current process.
  • Bitbucket for source control (I also use Atlassian's excellent SourceTree as a Git/Hg client.)

It's been working well for me, the only issue I have is duplication between the tools, and failing to consistently use Trac for defect tracking. What keeps me in Trello is how quick and easy it is to add items to it, and the fact that I'm using it as a catch-all - I can put a defect or an idea or a task into it in a couple of seconds; I just have to replicate it to the appropriate place later, which is the problem.

I think the issue boils down to being torn between having a centralized repository for "stuff to be done" (Trello) and having dedicated repositories catered to each type of thing to be done (Prognosticator, Trac, and WorkFlowy); and convenience. Trello is excellent for jotting something down quickly, but lacks the additional specific utility of the other tools for specific purposes.

I think what I'll end up doing is creating a "whiteboard" list in WorkFlowy, and using that instead of Trello to jot down quick notes when I don't have the time to use the individual tools; then I can copy from there to the other tools when I need to. That will allow me to cut Trello down to basically being a Kanban board.

2013-11-04

Behold, My Newest Creation!

I'm entirely too proud too announce my latest creation, Rogue Prognosticator. This is a web-based application for doing project estimation and schedule for software development. I've written about these topics before, and rest assured I will again; you can count on the concepts you see discussed here being taken into account in the software.

Right now the site is in open beta, free for public use. As features are added, some may be subscriber-only, or may start out being subscriber-only.

This project breaks a lot of new ground for me, and I've learned a lot already.

  • It's my first project from scratch using ASP.NET MVC or Entity Framework.
  • It's my first personal project in production using C# or .NET.
  • It's the first time I've used Windows Azure.
  • It's the first time I've used UserVoice.
  • It's the first time I've used continuous deployment from Git on a personal project in production.
  • It's the first time I've used SQL Server on a personal project in production.
  • It's the first time I've used WordPress in production.
This project was, as you may have guessed, the source for my post on entity framework model queries, as well as my post on value-based prioritization.

I've been using the project as I've been building it, and it's already been an excellent tool for me. Prioritizing features by estimated return was a particularly enlightening experience; it really helped me to get an objective look at the near-term plan and organize development more effectively.

I'll still talk here about the nitty-gritty of development but official product announcements will be coming through the product blog, Rogue Prognostications. I hope that others will find this project as useful as I have. Please feel free to drop any comments, questions, suggestions, or other feedback on the Rogue UserVoice.

More to come - watch this space!

2013-10-29

Simple Entity Framework Model Structure

I'll say right up front, I don't have a lot of experience with Entity Framework, and this could either be a well-known solution or a completely foolish one. All I know is that, so far, it has worked extremely well for my purposes.

Coming from the Java world, I'm used to using DAO's to serve as an abstraction layer between the controllers and the database, with the basic CRUD methods, plus additional methods for any specific queries needed for a given entity type.

Conveniently, entity framework provides a fairly complete DAO in the form of DbSet. DbSet is very easy to work with, and provides full CRUD functionality, and acts as a proxy for more complex queries. I wanted to keep queries out of my logic, however, and in the model.

Looking at it, I didn't want to have to write an entire wrapper for DbSet, and subclassing it seemed like asking for trouble. That's when it occurred to me to use extension methods for queries. It turns out you can define extension types against a generic type with a type argument specified (e.g. this IEnumerable). This not only allowed me to abstract out the queries and keep them in the model, without having to wrap or subclass anything; but by defining the extensions on IEnumerable instead of DbSet, I have access to my queries on any collection of the appropriate entity type, not just DbSet. I can then chain my custom queries in a very intuitive and fluid way, keeping all of the code clean, simple, and separate.

For example, I have a table of tags. I've created extension methods on IEnumerable to filter to tags used by a given user, and to filter by tags starting with a given string. I can chain these to get tags used by a given user and starting with a given string. I can also use these queries on the list of tags associated with another entity, as IList implements IEnumerable, and thus inherits my query extension methods.

I don't know if this is the best way - or even a good way - but it's worked for me so far. I do see some possible shortcomings; mainly, the extensions don't have access to the context, so they can't query any other DbSets, only the collection it's called against. This means that only explicit relationships can be queried against, which hasn't been a roadblock so far in my (admittedly simple) application. I'm not sure this is really a drawback though - you can still add a parameter to pass in an IEnumerable to query against, which again offers the flexibility to pass a DbSet or anything else.

2013-10-18

Pragmatic Prioritization

The typical release scheduling process works something like this:

  1. Stakeholders build a backlog of features they'd like to see in the product eventually.
  2. The stakeholders decide among themselves the relative priority of the features in the backlog.
  3. The development team estimates the development time for each feature.
  4. The stakeholders set a target feature list and ship date based on the priorities and estimates.

The problem here is primarily in step 2; this step tends to involve a lot of discussion bordering on arguing bordering on in-fighting. Priorities are set at best based on a sense of relative importance, at worst based on emotional attachment. Business value is a vague and nebulous consideration at most.

I propose a new way of looking at feature priorities:

  1. Stakeholders build a backlog of features they'd like to see in the product eventually.
  2. The stakeholders estimate the business value of each feature in the backlog.
  3. The development team estimates the development time for each feature.
  4. The stakeholders set a target feature list and ship date based on the projected return of each feature - i.e., the estimated business value divided by the estimated development time.

This turns a subjective assessment of relative priorities into an objective estimate of business value, which is used to determine a projected return on investment for each feature. This can then be used to objectively prioritize features and schedule releases.

I've been using this workflow recently for one of my upcoming projects, and I feel like it's helped me to more objectively determine feature priorities, and takes a lot of the fuzziness and hand-waving out of the equation.

Shameless self-promotion: Pragmatic prioritization is a feature of my project scheduling and estimation tool, Rogue Prognosticator

2013-09-17

JComboBox with Custom ComboBoxModel Not Updating Value on setSelectedItem()

I wrestled with this issue for some time before figuring out the cause, so I hope this helps someone out there. I had a JComboBox with a custom ComboBoxModel. Everything was working fine, except a call to setSelectedItem would do everything it should (fire events, update the selected item property) except it wasn't updating the displayed value in the JComboBox itself. Clicking the drop-down would even show the correct item selected, and getSelectedItem() returned the correct result; it was just the box itself that was wrong.

Apparently the displayed value in the JComboBox isn't based on getSelectedItem(), but rather on the top item in the list. I don't know how or why this is, or if it's due to some intricacies of my GUI code, but bringing the selected item to the top of the ComboBoxModel's item list when calling setSelectedItem fixed the issue. Go figure.

If anyone has any insight into what causes this, please drop a comment!

2013-09-10

Qaudropus Rampage

I've taken up the excellent indie mobile title (and product of the 7-day roguelike challenge) Quadropus Rampage. It's an all-around excellent title, with some hilarious content, solid gameplay, and excellent replayability. It's free to play, with in-app purchases, and one of few cases where I've made an IAP in order to support the developers.

I haven't been playing long, and I haven't beaten it, but I thought I'd toss out a few tips, tricks, and strategies I've learned along the way.

Mechanics:

  • Attack has longer range than you think, and different weapons have different ranges.
  • Hold down attack to get a spin attack, that damages all enemies around you. You'll end up turned about 60 degrees counter-clockwise from the direction you were facing when you started the spin. 
  • Note that the spin attack deals less damage than your normal attack. Note also that you still get the normal attack triggered by pressing the attack button, in order to charge up the attack. This means you can strike, holding down the button, then release, to get a quick one-two combo. Practice the timing of holding down the attack button, it can make a huge difference in crowded maps.
  • Smash attack does a ton of damage in a radius similar to the spin attack (farther with upgrades & masteries), as well as knocking enemies back (and possibly off ledges.)
  • Dodge lets you move over empty spaces and even off the edges of the map. You can hold down the dodge button to continue flying around the map until you release it.
  • Bubble gives you a temporary shield that blocks all damage until it expires.
  • Bingo flings himself toward a random nearby enemy every few seconds. If there are enemies grouped together, or in a line, he will damage every enemy he passes through. He does a lot of damage, and can crit.
  • The Rage meter (top of the screen) fills up as you deal damage to enemies, and rapidly depletes over time. If it gets to full, you enter a Rampage, dealing bonus damage and taking reduced damage from enemies. In order to enter a rampage, you'll have to continuously dish out damage long enough to fill the meter before it starts to fall. This gets easier with more upgrades, and at lower depths (when there are more enemies to work with.)

Techniques:
  • Most levels I start by dodging into the middle of the map, trying to lure as many enemies as possible into a central area, then I smash attack to take out as many as I can at once, and knock the rest away from me to get some breathing room.
  • Dodging toward an enemy and then attacking is an excellent way to deal damage without taking any yourself. You can dodge in, attack, and dodge back out if the attack isn't enough to kill.
  • Against large enemies, you can always run up, bubble, and hack away at them continuously until the bubble expires, then dodge away.
  • Heartfish move pretty slowly, but they do follow you. If you're in trouble, dodge toward them to grab them, or bubble then dodge so you can grab them without dying on the way. If you're at or near full health, dodge away from them toward your enemies, to avoid picking them up until you actually need them.
  • The most important weapon stats are health and damage; everything else is nice, but not nearly as important. Weapon size also plays a part, but generally speaking, just look for weapons where the top two stats (damage and health) are green (better than what you have now.) Learn to swap weapons quickly in the midst of a melee when you find  a better weapon.
  • Depth charges are excellent tools, but can be difficult to use properly. They always appear at the edges/corners of the map, so often the best technique is to dodge off the edge of the map, come at the depth charge from the far side, then smack it toward your enemies. The same basic techniques for the depth charges apply to Bingo's ball as well.
  • If you smash attack off the edge or through a hole, you'll land in the next depth with a smash attack. If you have the mastery upgrade that refreshes your smash attack cooldown on each depth, you'll land with a smash attack and no cooldown. This makes it a viable strategy, if you end a level with full health and full smash, to smash off the edge of the map, destroy what you can when you land, dodge off toward another group of enemies, and smash attack again. At later depths, this is almost certain to trigger a rampage, letting you clean up the level in no time.


Upgrades:
  • Strength, Vitality, and Smash are the most important skills; invest in these first. I did it round-robin in that order (Strength 1, Vitality 1, Smash 1, Strength 2, etc.) and it worked well for me.
  • Next most important are probably Bingo and Bubble, in that order.
  • Rampage isn't the least important, however, it doesn't really come into play until the lower depths, and until you've got the other skills levelled up enough.
  • Keep in mind what upgrade you want next and how much it costs; you can pause mid-game and buy the upgrade as soon as you can afford it. You aren't limited to purchasing upgrades between games.

Masteries:
  • Masteries are a combination of upgrade and achievement. When you hit a certain goal, the mastery will be unlocked, and you'll get the option of two upgrades for each mastery, which you can switch between at any time (including mid-game).
  • Any time you get an achievement while playing, it's a good idea to pause, go into the character screen, and choose an upgrade for that mastery, to gain the bonus as soon as possible (neither option is selected by default, you must select one yourself to gain any benefit.)
  • Keep in mind that you can switch mastery bonuses mid-game as well if you need to. I've not run into a situation where this would be needed.
  • Many of the masteries will happen when they happen, but most can be achieved with considered action. I strongly recommend picking a mastery and focusing on it during your gameplay; for example, focus on using your smash attack as often as possible until you get that mastery, or focus on dodging over and over and over until you get that mastery, and so on.
  • None of the mastery bonuses are game-changing, but many are very good, and the combination of a few of them, plus some upgrades, quickly make the first few depths a cakewalk.

Pets:
  • You can have two pets active at a time, not including Bingo. Bingo is always active, and does not count as a pet. Likewise, the Bingo upgrades don't affect your other pets.
  • I've only used Cy and Saww, but both have been very effective for me, though I'm considering swapping Cy for Smiles.
  • It doesn't seem like there's a significant imbalance between them, I think it's mainly a matter of personal preference and play style.

Artifacts/Grubby:
  • Don't bother purchasing anything from Grubby until you've maxed out all the upgrades. Your orbs are better spent there. You're very unlikely to beat the game without maxed upgrades, no matter how many fancy items you pick up from Grubby.
  • Many of the artifacts just give a 20% increase to damage to a particular type of enemy. These are nice, but not worth paying orbs for to buy them from Grubby.
  • The best artifacts, in my experience, are Heartfish Food, Fountain Pen, Bingarang, Forn Orb, Third Eye, Bermuda Triangle, Bingo Unchained, Spiked Collar, Star Biscuit, Embiggener, Urchin Spines, Gorgo's Shovel, and particularly Lucky Coin (free resurrection!).
  • Don't waste your orbs on buying weapons from Grubby unless a) it's ridiculously better than anything you've seen at your current depth, and/or b) you're within two depths of facing off against Pete. The rest of the time, it's just not worthwhile unless you have so many orbs you don't care any more.

Purchases:
  • You can purchase orbs (for buying upgrades and buying items from Grubby in game) and dubloons (for buying pets and resurrections) in the in-app store. These are relatively cheap compared to most games with similar freemium models.
  • Any purchase will earn you a new starting weapon that's significantly better than the starting tennis racket; in fact, if you make a purchase, your new starting weapon will last you the first several depths easily.
  • Don't waste your dubloons on unlocking masteries; they generally aren't worth what they cost in dubloons, especially since you can earn them through playing anyway.

Synergies: some things just work particularly well in combination. For example:
  • All Dodge upgrades, Saww, Flurry upgrade from Quick mastery, Inksplosion upgrade from Nimble mastery, and Fountain Pen: dodge to kill. You cause an explosion (dealing damage and causing knockback) when you start a dodge, you get bonus damage when you end a dodge, and both you and your pet deal damage during a dodge.
  • All Rampage upgrades, either upgrade from the Brawler mastery, Supple Crits upgrade from the Hulk mastery, Bingarang, Forn Orb, Eye Patch, and Third Eye: ultimate rampage. You shoot lasers out of your face. Bingo shoots lasers out of his face. He does this while spinning continuously around the map until the rampage ends. And you rampage more often.What's not to love? If you take Pain Tolerance from Brawler, and have some or all of the above dodge stuff, you can indiscriminately fly around the map lasering everything in sight while taking reduced damage. Alternatively, take the I'm Always Angry upgrade to rampage more often.
  • Saww, Smiles, Bingarang, Forn Orb, Bingo Unchained, Spiked Collar, Star Biscuit, and Urchin Spines: let the pets do the work. Park yourself in an urchin for safety, dodging briefly to keep Saww going.

2013-08-04

Assumptions and Unit Tests

I've written previously on assumptions and how they affect software development. Taking this as a foundation, the value proposition of unit testing becomes much more apparent: it offers a subjective reassurance that certain assumptions are valid. By mechanically testing components for correctness, you're allowing yourself the freedom to safely assume that code which passes its tests is highly unlikely to be the cause of an issue, so long as there is a test in place for the behavior you're using.

This can be a double-edged sword: it's important to remember that a passing test is not a guarantee. Tests are written by developers, and developers are fallible. Test cases may not exercise the behavior in precisely the same way as the code you're troubleshooting. Test cases may even be missing for the particular scenario you're operating under.

By offering a solid foundation of trustworthy assumptions, along with empirical proof as to their validity, you can eliminate many possible points of failure while troubleshooting, allowing you to focus on what remains. You must still take steps to verify that you do have test coverage for the situation you're looking at, in order to have confidence in the test results. If you find missing coverage, you can add a test to fill the gap; this will either pass, eliminating another possible point of failure, or it will fail, indicating a probable source of the issue.

Just don't take unit test results as gospel; tests must be maintained just like any other code, and just like any other code, they're capable of containing errors and oversights. Trust the results, but not the tests, and learn the difference between the two: results will reliably tell you whether the test you've written passed or failed. It is the test, however, that executes the code and judges passing or failing. The execution may not cover everything you need, and the judgement may be incorrect, or not checking all factors of the result.

2013-07-04

Feature Disparity Between Web, Mobile, and Desktop

I understand that mobile is new territory, and that web applications have certain restrictions on them (though less and less so with modern standards and modern browsers), but it seems very strange to me that there are still such glaring disparities between the web, mobile, and desktop versions of some products - even products designed with mobile in mind.

Take Evernote as an example. It's been out for Android for ages, with regular new releases offering new features and functionality. Yet there are still basic features that are not available in the mobile client, including strike-through text, horizontal rules, alignment, and font face/size changes. If you have a note with these features, and you edit the note in the Android app, you get a friendly warning that the note contains unsupported features, and the editor forces you to edit paragraph-by-paragraph, like the old and irritating Google Docs app for Android. I find this more than a little bit ridiculous; why are you adding new, nice-to-have features when basic functionality is still unsupported?

Look at Google Keep for the opposite example. The mobile app allows reordering the items in a checklist with drag-and-drop. The web app doesn't allow you to reorder items. The only way to reorder items is using cut and paste. This is something you can absolutely achieve in a web app, and they've done it before, but for some reason that one, basic, important feature is just somehow missing.

The Mint mobile app allows changing budgets, but not changing whether or not the budget surplus/deficit should roll over month-to-month, which you can do in the web app. It's most of the feature, just missing one little part that can cause frustration because if most of the feature is there, you expect the whole feature to be there.

The GitHub web app doesn't even include a git client - the closest you can get is downloading a repo, but you can't actually check out and manage a working copy.

The Google Maps app for Android doesn't allow editing your "My Maps", or to choose from (or create) alternate routes when getting directions. It also doesn't include the web version's traffic forecasting. The Blogger web app is next to useless; editing a note created on the desktop gives you a WYSIWYG editor with the plain text littered with markup, and writing a post on mobile and then looking at it on desktop shows that there's some serious inconsistencies with handling of basic formatting elements like paragraphs. Don't even get me started on the useless bundle of bytes that is the Google Analytics Android app; it's such a pathetic shadow of the web application that there's no point in even having it installed.

These seem to me like cases of failure to eat your own dog food. If there were employees - especially developers or product managers - of these companies, using these applications on each supported platform, these issues would have been solved. They're the sorts of things that look small and insignificant on a backlog until they affect you on a day-to-day basis; those little annoyances, repeated often enough, become sources of frustration.

2013-06-11

Teaching a Developer to Fish

I write a lot about development philosophy here, and very little about technique. There are reasons for this, and I'd like to explain.

In my experience, often what separates an easy problem from an intractable one is method and mindset. How you approach a problem tends to be more important than the implementation you end up devising to solve it.

Let's say you're given the task of designing a recommendation engine - people like you were interested in X, Y, and Z. Clearly this is an algorithmic problem, and a relatively difficult one at that. How do you solve it? 

The algorithm itself isn't significant; as a developer, the algorithm is your output. The process you use to achieve the desired output is what determines how successful you'll be. I could talk about an algorithm I wrote, but that's giving a man a fish. I'd much rather teach a man to fish.

So how do you fish, as it were, for the perfect algorithm? You follow solid practices, you iterate, and you measure. That means you start with a couple of prototypes, you measure the results, you whittle down the candidate solutions until you have a best candidate, and then you refine it until it's as good as it can get. Then you deploy it to production, you continue to measure, and you continue to refine it. If you code well, you can A/B test multiple potential algorithms, in production, and compare the results.

How do you fish for a fix to a defect? You follow solid practices, you iterate, and you measure. You start by visual inspection, checking for code quality, and doing light refactoring to try to simplify the code and eliminate points of failure, to narrow down the possibilities. Often this alone will bring the root cause of the defect to the surface quickly, or even solve it outright. If it doesn't, you add logging, and you watch the results as you recreate the error, trying to recreate it in different ways, to assess the boundaries of the defect; if this is for an edge case, what exactly defines the "edge" that's affected? What happens during each step of execution when it happens? Which code is executing and which code isn't? What parameters are being passed around?

In my experience, logging tends to be a far more effective debugging tool than a step-wise debugger in most cases, and with a strong logging framework, you can leave your logging statements in place with negligible performance impact in production (with debug logging disabled), and with fine-grained controls to allow you to turn up verbosity for the code you're inspecting without turning all logging on and destroying the signal-to-noise ratio of your logging output.

You follow solid practices, you iterate, and you measure. If you use right process, with the right mindset, you'll wind up at the right solution.

That's why I tend to wax philosophical instead of writing about concrete solutions I've implemented. Chances are I wrote the solution to my problem, not your problem; and besides, I'd much rather teach a man to fish than give a man a fish.

2013-06-08

My Present Setup

I thought I'd take a quick moment to lay out my current setup. It's not perfect, it's not top-of-the-line (nor was it when any of the parts were purchased), it's not extravagant, but I find it extremely effective for the way I work.

The Machine (DIY Chronos Mark IV):

  • Intel Core i5 750 LGA1156, overclocked from 2.6GHz to 3.2GHz
  • ASRock P55 Extreme
  • 8GB DDR3 from GSkill
  • ATi Radio HD 5870
  • 256GB Crucial m4 SSD (SATA3) - OS, applications, caches & pagefile
  • 2 x 1TB Seagate HDD - one data drive, one backup drive
  • Plextor DVD-RW with LiteScribe
I find this configuration to be plenty performant enough for most of my needs. The only thing that would prompt an upgrade at this point would be if I started needing to run multiple VM's simultaneously on a regular basis. The GPU is enough to play my games of choice (League of Legends, StarCraft 2, Total War) full-screen, high-quality, with no lag. The SSD keeps everything feeling snappy, and the data drive has plenty of space for projects, documents, and media. The second drive I have set up in Windows Backup to take nightly backups of both the primary and data drives.

My interface to it:
  • Logitech G9x mouse (wired)
  • Microsoft Natural Elite 4000 keyboard (wired)
  • 2 x Dell U2412M 24" IPS LCD @ 1920x1200
  • Behringer MS16 monitor speakers
If you couldn't tell, I have a strong preference for wired peripherals. This is a desktop machine; it doesn't go anywhere. Wireless keyboards I find particularly baffling for anything other than an HTPC setup; the keyboard doesn't move, why would I keep feeding it batteries for no benefit? The mouse is an excellent performer, and I love the switchable click/free scroll wheel (though I wish the button weren't on the bottom).

The displays are brilliant and beautiful, they're low-power, I definitely appreciate the extra few rows from 1920x1200 over standard 1080p, and having two of them suits my workflow extremely well; I tend to have one screen with what I'm actively working on, and the other screen is some combination of reference materials, research, communications (chat, etc.), and testing whatever I'm actively working on. Particularly when working with web applications, it's extremely helpful to be able to have code on one screen and the browser on the other, so you can make a change and refresh the page to view it without having to swap around. These are mounted on an articulated dual-arm mount to keep them up high (I'm 6'6", making ergonomics a significant challenge) and free up a tremendous amount of desk space - more than you'd think until you do it.

The Behringers are absolutely fantastic speakers, I love them, to death, and I think I need to replace them. I recently rearranged my desk, and since hooking everything back up, the speakers have a constant drone as long as they're turned on, even with the volume all the way down. I've swapped cables and fiddled with knobs and I'm not sure the cause.

The network:
  • ASUS RT-N66U "Dark Night" router
  • Brother MFC-9320CW color laster printer/scanner/copier/fax (on LAN via Ethernet)
  • Seagate 2TB USB HDD (on LAN via USB)
The RT-N66U or "Dark Night" as it's often called is an absolutely fantastic router. It has excellent wireless signal, it's extremely stable, it's got two USB ports for printer sharing, 3G/4G dongle, or NAS using a flash drive or HDD (which can be shared using FTP, Samba, and ASUS' aiDisk and aiCloud services). The firmware source is published regularly by ASUS, it's Linux-based, and it includes a complete OpenVPN server. It offers a separate guest wireless network with its own password, which you can throttle separately and you can limit its access to the internal network. It has enough features to fill an entire post on its own.

Mobility:
  • Samsung Galaxy S4 (Verizon)
  • ASUS Transformer Prime (WiFi only)
The SGS4 is an excellent phone, with a few quirks due to Samsung's modifications of the base Android OS. The display is outstanding, the camera is great, the phone is snappy and stable, and it has an SD card slot. That's about all I could ask for. The tablet I bought because I thought it would make an excellent mobile client for my VPN+VNC setup; unfortunately, I've had some issues getting VNC to work, and now that I'm on a 3840x1200 resolution, VNC @ 1080p has become less practical. However, it still serves as a decent mobile workstation using Evernote, Dropbox, and DroidEdit.

All in all, this setup allows me to be very productive at home, while providing remote access to files and machines, and shared access to the printer and network drive for everyone in the house. The router's NAS even supports streaming media to iTunes and XBox, which is a plus; between that, Hulu, and Netflix, I haven't watched cable TV in months.

2013-06-07

Code Patterns as Microevolution

Code patterns abide by survival of the fittest, within a gene pool of the code base. Patterns reproduce through repetition, sometimes with small mutations along the way. Patterns can even mate, after a fashion, by combining them, taking elements of each to form a new whole. This is the natural evolution of source code.

The first step to taming a code base is to realize the importance of assessing fitness and taking control over what patterns are permitted or encouraged to continue to reproduce. Code reviews are your opportunity to thin the herd, to cull the weak, and allow the strong to flourish.

Team meetings, internal discussions, training sessions, and learning investments are then your opportunity to improve both the quality of new patterns and mutations that emerge, as well as the group's ability to effectively manage the evolution of your source, to correctly identify the weak and the strong, and to have a lasting impact on the overall quality of the product.

If you think about it, the "broken windows" problem could also be viewed as bad genes being allowed to perpetuate. As the bad patterns continue to reproduce, their number grows, and so does their impact on the overall gene pool of your code. Given the opportunity, you want to do everything you can to make sure that it's the good code that's continuing to live on, not the bad.

Consider a new developer joining your project. A new developer will look to existing code as an example to learn from, and as a template for their own work on the project, perpetuating the "genes" already established. That being the case, it seems imperative that you make sure those genes are good ones.

They will also bring their own ideas and perspectives to the process, establishing new patterns and mutating existing ones, bringing new blood into the gene pool. This sort of cross-breeding is tremendously helpful to the overall health of the "code population" - but only if the new blood is healthy, which is why strong hiring practices are so critical.

2013-06-06

The New GMail for Android

The new GMail for Android UX sucks. I mean... it's really awful.

They've replaced the checkboxes next to each message (useful) with sender images (gimmick), or, if there is no sender message (i.e., everything that's not a G+ contact - so, every newsletter, receipt, order confirmation, etc. you'll ever get), a big colorful first initial (completely useless waste of space). This image then acts as if it were the checkbox that used to be there (confusing) for selecting messages. You can turn off the images, but you don't get the checkboxes back; you can only tap-hold to select multiple messages, though this isn't mentioned anywhere, you just have to guess.

They've gotten rid of the delete button (why?), and moved it to the menu.

If you have no messages selected, pressing the device's menu key gives you the menu. However, if you do have messages selected, the menu key does nothing, instead you must tap the menu button that appears at the top-right of the display. It's not there if you don't have messages selected.

Once you're viewing a message, there are two menus: one when you tap the menu button, with 90% of the options in it, and another at the top-right gives you just two options, forward and reply-all; this almost makes sense, except that it uses the same, standard "here's the menu" button that's used on (some) other screens as the *only* available menu.

In the message view they've also gotten rid of the delete button (to match the annoyance of the message list, I supposed).

There is also a new "label settings" screen that's fairly mysterious; I assume it applies to the current label, though this includes "Inbox", which - while I understand it's treated internally as a label - I think most users don't think of as being a label in the typical sense.

2013-06-02

Building a Foundation

It's been said that pharmaceutical companies produce drugs for pennies per pill - except the first pill, which costs millions. Things aren't so different in the land of software development: the first usage of some new functionality might take hours, building the foundation and related pieces. But it could be re-used a hundred times trivially, and usually expanded or modified with little effort as well (assuming it was well-written to start with).

This is precisely what you should be aiming for: take the time to build a foundation that will turn complex tasks into trivial ones as you progress. This is the main purpose behind design concepts like the single responsibility principle, the Hollywood principle, encapsulation, DRY, and so on.

This isn't to be confused with big upfront design; in face, it's especially important to keep these concepts in mind in an agile process, where you're building the architecture as you go. It can be tempting to just hack together what you need at the moment. That's exactly what you should be doing for a prototype, but not for real development. For lasting functionality, you should assemble a foundation to support the functionality you're adding now, and similar functionality in the future.

It can be difficult to balance this against YAGNI - you don't want to build what you don't need, but you want to build what you do need in such a way that it will be reusable. You want to save yourself time in the future, without wasting time now.

To achieve a perfect balance would require an extraordinary fortune teller, of course. Experience will help you get better at determining what foundation will be helpful, though. The more experience you have and the more projects you work on, the better sense you'll have of what can be done now to help out future you.