Over the last two years at Vistaprint, I’ve grown a lot – both as a developer and in general. I learned some lessons through mistakes I’ve made, things that I saw being done correctly, and by observing processes that could have been more efficient.
Over the this and the next two blog posts, I’ll cover the lessons I learned in three different categories – software development, career and personal development, and lessons that could be applied to a company.
Software Development Lessons
Lots of little bugs waste lots of time
This is similar to Jeff Atwood’s “My Dog Patches” post. Sometimes it’s tempting to just fix the immediate symptom of a bug because it will be a quick fix. However, a day or a week later, a similar bug will pop up again. Even if these bugs only take a few minutes to cover up, if they have the same root cause, it’s probably worth just fixing the root.
I fell into the ‘patch, patch, patch’ trap at least twice at Vistaprint. The first one was on my first really large project when we were adding hats and dark t-shirts. After the initial implementation of the system that changed user colors based on their chosen substrate (so text that might be black on a white background would be white on black backgrounds, etc.), we started having a lot of bugs in which a user could do some complicated series of actions and end up with the wrong color text on a given background. We couldn’t tell if it was because the user had chosen that particular color or if we had an adjustment bug. Most of the times that the bug came up, we’d change some path through the site to keep track of user colors. Then, another bug would show up somewhere else. Eventually, I realized that I was wasting at least a few hours every week and in order to do more work, I needed to stop spending time on substrates.
In the end, I just took a day to think about the problem and came up with a system that split some information in user documents into two parts. This was a deep change in our model, but it allowed us to look at a document at any stage and prove an invariant – we could see what the original user-chosen colors were and that we were only overriding the non-customized colors with our substrate variants. Thinking about this problem from an invariant model helped me prove that we would have no more problems in the future and saved me hundreds of hours in the long run.
The second time I can think of was when I was trying to add a lot of new functionality to one of our controls. I decided to try to just add the new elements on top of the old code and modify that as little as possible. Afterward, I was going to have a phase two in which I’d merge the old code and the new code completely. However, the first version kept breaking and I kept patching local fixes to bugs in the old code that were causing bugs in the new code. Eventually, I had wasted a week and I missed the first release. At that point, I realized I just had to rewrite the old code from scratch. I spent three days and rewrote the whole control to use my new components even for the old functionality. In the process, I fixed a lot of existing bugs and finished the new requirements. I had wasted a week trying to get something done in a day, then continually seeing each fix as a three-hour event. Instead, I should have seen the bigger picture that I would have saved time by investing the three days up front and skipping the week of patches.
Code safely the first time
When I first started at Vistaprint, I was fresh out of school and had never worked on such a large application before. I ended up with a lot of null-pointer exceptions in my code because I kept assuming I would get valid values from other code. After a month of fixing these (particularly in javascript), I finally figured out that any time I have a question about a value, I should verify it first. It’s easy code to write and it will save so much debugging time. I also try to prove that APIs I write will always return valid values or have an explicit failure condition. This is a much more general problem than null pointers, but that’s what really made me start checking.
Use a third party framework where applicable
Vistaprint has a lot of home-grown javascript code which is really good and well written. However, many of it duplicates what’s available in open-source javascript frameworks like jQuery. Every time we had to fix a bug or performance problem in our framework, that was time we may have been able to avoid if we had been using code that has been built and tested by a community. Luckily, right as I was leaving, Vistaprint was starting to recognize this and move towards replacing some of our common code with jQuery.
Requirements will change and code will live longer than you expect
Make your work maintainable. If you are working at a large company, one thing you should always try is searching your code base for words like “temp” or “hack”. you’ll be shocked at how much throwaway code people thought they were writing two years ago that’s still in the code base.
In general, this isn’t terrible; sometimes the most important thing to do is to move forward with new work. However, when the hack is unmaintainable, it will keep costing you time and effort over and over for the rest of its lifetime. Vistaprint had a few places in the codebase that I can think of in particular that were written so quickly and haphazardly (large pieces of copy/paste, etc.) that they now suck the time out of anyone that has to modify them. It’s so hard to understand and to find all the places that have to be modified to make one small change that projects take twice as long as they should.
Retire old code early and often
Part of a project is retiring whatever it is the new project is replacing. At Vistaprint, I was stuck maining two APIs because we never had any time budgeted for upgrading the rest of the website to no longer use the legacy code. We constantly had bugs because other teams would still use the old APIs to access new documents, and the old APIs had too many abstraction violations and were no longer being updated with new features that we were adding in the new APIs. This went on for the entire two years and a half I was there. Next time I replace some code, I’ll make sure we budget time for proving that the new code works just as well as the old, then I’ll upgrade everything.
Backwards compatibility is a big concern
This is somewhat related to the last point, because even when you have a plan for upgrading, you will have overlap and you can’t break a user’s experience just because they’ve saved something in an old format. Even if your upgrade breaks some user’s information, you need to know how many and how valuable those items are. At Vistaprint, we had too many documents to upgrade all of them whenever we made a small change to the storage format, but we made sure that documents were upgraded while they loaded. We also had other ways to ensure backwards compatibility; it was never something we glossed over.
Large builds waste time
My friend Jim was fond of pointing out how much time we wasted at Vistaprint waiting for code to compile. With hundreds of projects in our build, any of which might break during the nightly update from SVN, I think he has a valid point. Even more than that, if we had used a technology that didn’t require a compilation step, we might be able to go through many more iterations of code in the same amount of time, because we could code, check localhost, code again without compile steps in the middle. My next environment will definitely be one that does not require compilations for all web code.
If you want to unit test, do it from the beginning
Coding for testability requires a different type of architecture than just coding for features. About a year into my tenure at Vistaprint, I tried to help out in a big push to add unit tests to our code, but I found adding tests required massive amounts of rearchitecture to allow us to mock important components. I was able to get some unit tests in, but the effort mostly faded out because of the difficulty in creating them.
I’m not entirely convinced of the utility of having comprehensive unit testing coverage, either. I hear a main benefit of unit tests is that they enable fearless refactoring, because you know the components still fulfill their contracts. However, most of the time we did any significant refactoring, it involved changing APIs and therefore the unit tests also had to be rewritten. If we’re rewriting the tests when we change the code, I’m not sure their actually preventing mistakes, since we’ll probably make a similar mistake in the test as we did in the code. I’ll probably go with a strong set of functional tests in my next project.
Performance counts, and applications should be performance tested
This can make a big difference to user experience, as well as affecting the bottom line of things like CPU usage. However, this is not to say that you should try to pre-optimize code while you write it. Some of the most productive days I spent at Vistaprint involved running code with a profiler. We were always able to significantly increase performance, and the places we made the performance gains were not necessarily where we would have guessed at first. Moreover, we were able to keep the code readable and maintainable while still increasing the performance. I read a great quote the other day in ‘Coders at Work’ – “It’s much easier to optimize correct code than it is to correct optimized code.”
When optimizing, know when to stop
This applies to optimizing anything, not just performance. If you get sucked into making something the ‘best possible’, you can quickly reach diminishing returns and end up paying for it in other ways. I did this one time when trying to determine the best algorithm for manipulating images to print correctly on hats. I spend a week trying different parameters and algorithms to make the image perfect, but in the end, my final result was hardly noticeably different than what I had on the second day. If I’d stopped then, no one would have been less satisfied, and I would have had three days to work on other projects.


February 10, 2010 at 12:23 pm
“Patch, patch, patch” is how engineers code
. Or at least the ones in my office. They know they should abandon their legacy code, but at this point, we would pretty much have to start all over. It’s hard to convince anyone to do that since the software is more a PR tool than a money maker (it’s sort of half and half), but it looks like it’s 10 years old and half of it is in fortran … I’m worried about what it what it will look like and how it will run in 10 more years …