Every so often you come across a discussion amongst developers that talk about eking every last drop of performance out of your code. I understand this mindset. It’s natural to want your code to perform as quickly and efficiently as possible. People don’t like to wait, and if we make them wait, they won’t like our application. If they don’t like our application, it’s kind of like we failed. No one likes to fail. Too, wait time is one of the easiest low-hanging fruit by which our code can be measured – “that must be bad code, look how long it takes to load a page.”
It’s time to call bullsh*t.
Performance is important, sure, but it’s not as important as many other things we, as developers, need to be concerned with. Once you reach a threshold of “acceptable” performance (more on this later), any time spent optimizing your code for faster load times is likely wasted effort. There are far more important things to spend that time on.
What else do we need to be concerned with? Lots of stuff:
- Application maintainability
- Overall end user experience (of which acceptable performance is just one piece)
This list isn’t in any particular order, and I’m sure you can come up with a few more things to add to it, but it gives you an idea of the types of things that you should be thinking about once your performance is “good enough.”
Where I’d rather spend my time
Of all the items in the list above, one area where I think people don’t spend enough time by a wide margin is application maintainability. Hopefully your application has a long life. Hopefully people use it for a long time and ask for new functionality or enhancements. In that time, they’ll likely come across a couple of bugs as well.
When this lifetime is measured in years, the hygiene and developer-friendliness of the original codebase are critically important. Too often, in the rush to hit a deadline, we cut corners in our development. Unfortunately, this makes our applications harder to maintain. How often have you inherited code from another developer, or had to revisit your own code 6 months after you wrote it, only to realize what a steaming pile of doo-doo you’ve got on your hands? I find myself in this situation fairly often and it’s rarely fun. How often have you had to spend an exorbitant amount of time debugging the codebase to find out exactly what is going on and the exact conditions that cause a bug to surface before you can even begin fixing it?
Wouldn’t it be better if instead of being dense and hard to follow, our code opened up before us like a road map and we could easily discern exactly what was going on? Wouldn’t it be better if, instead of spending hours tracking down exactly where in 20,000 lines of code we had to make a change to fix a bug, we could simply examine the logs and have them lead us exactly where we need to go?
That’s code that was written with an eye towards maintainability.
Here’s an example of what I’m talking about (click the image to enlarge):
This is an excerpt from the ULS logs of a sample application I wrote to demonstrate application maintainability. I added some color highlighting to the image to help you understand what is being written to the log:
Green is simple messages written into the logs to help a troubleshooter know what is going on and where in the code base this code came from
Blue is variable and parameter values. How nice is it to see actual values that can help you understand that a bug only shows up when a variable is greater than 100 and less than 200, for example. Seeing actual runtime values in the log can be invaluable.
Yellow is process trace message – one for each method I enter and again when I leave the method. Notice, too, that I get input and return parameter values for each method entry/exit (highlighted in blue).
What does all of this logging cost me in terms of performance? Roughly a tenth of a millisecond or so for each message. In other words, the 13 messages written for each iteration of my code (one iteration is shown above) cost me about 1.4 thousandths of a second:
In exchange for this miniscule amount of time, I get the rich details about the processing of my application. I’ll make that trade any day of the week and twice on Sundays.
I’ve been using this approach for projects lately and my current client is impressed with the speed with which I am able to resolve bugs. They almost don’t care that the bugs are showing up because I can get them resolved so quickly.
I’m not saying that this is perfect and I’m still tweaking the code that manages all of this for me (to be released as part of the CKS:API project I started last year) but I wanted to present it as an example of what is possible to give you an idea of what I’m talking about when I talk about maintainability as being more important than performance.
One final note on logging. To avoid filling up my log and slowing things down just a little more during routine production run time, I make use of the secret-squirrel VerboseEx logging level on the TraceSeverity enumeration. This level cannot be set through the UI so you need to do it in code. If a bug surfaces, I crank up the logging level and have the user repeat the steps that produced the bug. From my code’s point of view, nothing is different so we don’t have any issues with Heisenbugs.
More than Just Logging
The example above is simply logging and that is an important part of code maintainability, but it is not all there is. There is also:
Code comments – the oft-maligned part of a developers job. Code comments are important to help understand a codebase. I’m finding that, while I still comment my code, I do so less because the extra logging messages I’m adding can stand in for code comments. One benefit of this is that as my codebase evolves, I’m far more likely to update the inline process trace messages than I ever updated my comments. My comments tend to stay focused on big-picture elements of describing what a method does overall rather than the details of how it does it.
Defensive Coding – making sure that my code can continue running before attempting to do so and that the environment hasn’t changed in such a way that assumptions made by my code are no longer valid. Some of this is hinted at in the image above – the last green highlighted line says Contract satisfied – continuing with update. In my code, I’m checking both pre-conditions and post-conditions for my methods on entry/exit, as well as in the midst of processing to make sure that, for example, the list I need to work with hasn’t been deleted, or that my SPQuery has, in fact, returned items:
Don’t worry about the syntax there (the TrueIf object is another part of CKS:API that will be released publicly as soon as I can finish it). Just understand that I’m making sure I have a valid list item collection that contains items before I attempt to do anything with it. Does this cost me a few processor cycles? Sure, but it makes my code far more bulletproof and maintainable. While I don’t have numbers for this, I’d guess that all of this checking costs me no more than a handful of milliseconds.
Standards – I don’t particularly care what standards you code to, and honestly, no one outside your team should, either. The only thing that matters is that you as a team have standards and you adhere to them as closely as possible. BY standards, I’m talking about things like variable and method naming, approach to testing, commenting, etc. Within a project, I would recommend that you support the same standards. Cross project, it doesn’t so much matter. But it sure would be nice if all of your variables were named following the same pattern within a project. It just makes maintenance SO much easier.
So what is “acceptable” performance? The answer is invariably “it depends.” Mostly it depends on the content you’re serving up. If you are the only source for that information, which is often the case with intranets – SharePoint’s most typical use, then you’ve got some leeway. This doesn’t mean you have license to abuse your users, you still need to stay within the realm of reasonable response times, but you’ve got more time than, say, a public facing website.
Your thoughts on this matter will almost certainly be different, but here are the metrics I shoot for:
- Site home page: 2-3 seconds
- Interior landing pages: 3-4 seconds
- View pages: 3-4 seconds
- Item pages: 2-3 seconds
- Documents: depends on the size of the document
Again, let me repeat myself on a few points:
- These are general guidelines I use, not hard-and-fast rules.
- These are for internally facing sites. Public facing websites are a whole different ballgame
I use these guidelines to help determine how to architect my solutions; how I know that I can have my code spend a little more time making the applications easier to maintain; how I know that I should optimize things a little bit more because I’m not hitting them. Everything related to performance is a trade-off with something else. Knowing when I have some leeway is important.
Fixing Performance Problems
So far, everything I’ve mentioned so far falls into the bucket of slowing down your site (if only by a few hundred milliseconds). If you already have a performance problem, what options do you have?
Performance problems are fixable
But not always via code fixes. Sometimes that means adding more hardware – servers, RAM, CPU, faster disks, etc. Think about it this way – if you pay a developer $150/hour to fix performance problems and you estimate it requiring 80 hours, that will cost you $12,000. Depending on the nature of the problem, that money may be better spent simply increasing the RAM in your web servers or database servers.
Not all problems can be fixed simply by throwing more hardware at the problem. The trick is figuring out whether your particular performance problem is one that can. In general, more hardware will help if your servers (web or database) are heavily utilized and it is legitimate utilization. More hardware probably isn’t the right answer if your performance problems are the result of simply shoddy coding practices – not disposing of objects, poor use of collections, extra database hits, etc.
Perception vs. Reality
In few other places in our line of work is the distinction between perception and reality more important than a discussion about performance. The perception of speed is far more important than actual speed. If your users think your site is fast, then it’s fast; even if a stopwatch says differently.
Am I saying that you shouldn’t make your code run efficiently and as quickly as possible? No. Absolutely not. I’m saying that you shouldn’t be a slave to performance. There are more important things to worry about.
Call to Action
The next time you need to write some code, think about performance in light of everything else. Think about how adding some code that will reap huge benefits during maintenance or bug fixing are well worth the 100 or 200 milliseconds of processing time they take on page load. Actively work to make your code more maintainable and don’t worry quite so much whether a page take 2 or 2.5 seconds to load. No one will notice, except the developer who has to figure out what’s going on in the code 6 months later – the developer that may be you.