For the longest time in places like Java's IRC channel, SO, and other places I've been told something along the lines of "Worry about how the code looks and its readability/understandability now, and performance later if absolutely necessary". So for the longest time, I haven't really been OCD about performance for my small desktop or web apps, just removing the obviously inefficient.

Most responses are "What about scalability?". Thats a legitimate point, but if my app was only built to parse, say, files 10,000 lines long, should I make my code a mess for the small percentage of people that are going to shove in a 1,000,000 line file?

My main question is when should I trade the easy but somewhat inefficient ways of doing tasks for big giant complicated beasts that do things extremely quickly but destroy any possible ways of upgrading and make the code excessively difficult and prone to rewriting anyway by the next developer?

7 Answers
7

If you write a small app to process 10,000 line files and you get a 1,000,000 line file every 100th file, it probably doesn't matter that it takes longer to process that one file. However, if you are regularly getting files that are 5-10 times larger than initially and your application is taking too long to do its job, then you start profiling and optimizing.

Now, I said "too long to do its job". That is up to the user or sponsoring organization to decide. If I'm doing a task and it takes me 5 minutes to do something when it took me 3 without the software or with a different tool, I'd probably file a bug report or maintenance request to have that improved.

If you are the user, how long you want your software to take to do its job is up to you - only you can decide if you want it done faster or if you are willing to wait longer to have more readable code.

My main question is when should I
trade the easy but somewhat
inefficient ways of doing tasks for
big giant complicated beasts that do
things extremely quickly but destroy
any possible ways of upgrading and
make the code excessively difficult
and prone to rewriting anyway by the
next developer?

This is usually a false dichotomy. You can write wonderfully efficient, readable and maintainable code. You can write wonderfully inefficient, unmaintainable piles of mess.

When dealing with performance issues, I usually try to think about the business problem I am solving. How will my software behave when my customers use it. Will my applications performance make Jacob Nielsen happy?

++ FALSE DICHOTOMY! Will they never learn? When you find and fix a performance problem, the code is not only quicker, it's better. I only regret that I have but one upvote to give!
–
Mike DunlaveySep 17 '10 at 15:31

+1 for writing that it's USUALLY a false dichotomy... not always, but usually.
–
YarMay 25 '11 at 12:53

1

-1 for writing it's usually a false dichotomy - fact is, it is usually correct, and only in rare cases a false dichotomy. In more than 30 years of my programming career I have seen too many "well intended" performance optimizations which in fact made the code harder to understand and maintain (and often optimized something which was totally unnecessary to be optimized).
–
Doc BrownAug 28 '12 at 6:15

A truism I picked up studying microprocessors in college that stayed with me: "Make the common case fast. Make the uncommon case correct."

As long as you have just a small percentage of users choking your code with input two orders of magnitude larger than what it was meant to handle, don't sweat it. Make sure it handles the input correctly if they give it long enough, and doesn't leave anything corrupted into uselessness if they kill the job before it finishes.

But, once more and more people start using it that way (or start telling you "You know, I'd dearly love to use that tool you wrote on my weekly TPS reports, but it takes all freakin' day"), that's when you start considering trading away ease of maintenance for performance gains.

My main question is when should I trade the easy but somewhat inefficient ways of doing tasks for big giant complicated beasts that do things extremely quickly but destroy any possible ways of upgrading and make the code excessively difficult and prone to rewriting anyway by the next developer?

"Worry about how the code looks and its readability/understandability now, and performance later if absolutely necessary" is the easy way out, and generally unhelpful. a good design will be easy to maintain, easy to read, and efficient.

performance is one common component of a good design. if your program is slow and wasteful, it's really not reusable. when you need to fix that mess, you force updates on your clients, unless it's just too time consuiming for them to update. that slow program becomes the big mess that is too costly to improve. then they choose an alternative becasue it does not suit their needs. diagnosing, updating, and dealing with side effects of improvements to a bad design often outweigh the initial development time of writing it to be efficient, work correctly, and has a genrally good design. that program is highly reusable and requires low maintentance (win).

so, the short answer to your question is "don't be wasteful. write for reuse. it's ok to be lazy when prototyping/developing proofs of concepts, but don't use that prototype for production code.".

do be aware of and avoid wasteful designs when writing production programs and programs that you intend to reuse. during implementation is the ideal time to write your program to not be wasteful - you have a clear idea of the details and its operation, and it's really painful and ineffective to fix after it's written. a lot of people believe a little profiling (maybe) at the end or if there is a problem is adequete, when it's usually too time consuming to redesign/change and the inefficiencies are so many and so widespread that you don't understand the program well based on the results of a profile. this approach takes little time during implementation and (assuming you have done this enough times) typically results in a design that is several times faster, and is reusable in many more contexts. not being wasteful, choosing good algorithms, giving thought to your implementations, and reusing the right implementations are all components of good design; all of which improves readability, maintainability, and reuse more often than hurts it.

Seriously, code should always be written so as to be easily understood and maintained.

With regard to when to deal with performance problems, deal with them once you identify them, don't pre-optimize your code because then you'll just be guessing about where the performance problems are.

If your code is written so as to be clear, concise, understandable, and maintainable then you or another programmer should have no problem refactoring the code to make it more efficient.

I don't agree with this. A performance requirement is a valid non-functional requirement for a system.
–
Thomas Owens♦Sep 1 '10 at 21:31

Technically if there is a clearly defined performance related requirement then it could be said that you have identified a performance problem and have to account for it in your solution. What I'm talking about is getting clever in advance so that you can avoid non-specific 'potential' problems.
–
Noah GoodrichSep 1 '10 at 21:45

Ah. Yeah, your absolutely right in that case. You don't worry about the possibilities because there are so many, but focus on what you know.
–
Thomas Owens♦Sep 2 '10 at 13:51

I normally write code to be readable first and foremost. If, and only if, I find that the program runs too slow to do its job, do I profile and optimise. That said, there is nothing wrong with getting into the habit of performing common optimisations that don't affect the readability of your code. That is, if a piece of code can be written in two equally (or nearly equally) readable ways, choose the one that's usually faster.

For example, in Python, list comprehensions (or generator expressions) tend to be faster than the equivalent for loop, so I use list comprehensions where I can, if they don't affect readability (for instance, I don't nest list comprehensions if I can avoid it, and use a for loop instead because nested list comprehensions can be hard to mentally parse).

Similarly, immutable data types tend to be faster than mutable ones, so I use immutable data types where I can.