Don’t Strive For Perfection
Posted by Davy Brion on January 4th, 2010
A mistake that you commonly see developers make (especially young ones) is to try to achieve perfection when they need to design something. Whether it’s just a reusable base class, a component, or even a small library or framework, a lot of people get too caught up in details that don’t really matter in most cases.
It is understandable though, and i think most developers have gone down that path quite a few times in their careers. Especially early on. You know how it goes… you need to create something that is going to be reused by others, and you want to make sure that it is just perfect. That nobody can say anything bad about it. That everyone will agree without discussion that that piece of code is simply great.
So a lot of people in that situation start thinking about things like:
- Shouldn’t i seal this class? Nobody’s ever going to need to derive from this because i’ve left plenty of other extensibility points
- Shouldn’t i seal this method? After all, nobody should ever change the way this particular piece of code works
- This class will never be used by anyone outside of this assembly, so i probably should make it internal, right?
- This particular method will be a bottleneck so i should really make it as fast as i can
- Whenever i can pull some common logic in a base class, or introduce more base classes i should do so!
- Etc…
The reality of the situation is that despite your best intentions, focusing too much on details like that will quite frequently lead to very inflexible code that a lot of people will find hard or annoying to use. Unless you are very experienced with this kind of stuff (and for the record, i’m not saying that i am) you’re very likely to get these decisions wrong if you think about them up front so it really isn’t worth spending so much time on ‘details’ like that. In fact, the best thing to do is often to keep things as simple as possible until you actually have a reason to make them more complicated. Making things more complicated than they need to be in advance never works, and you’re probably going to over-complicate things in places where it turns out not to matter. If you’re really unlucky, that will actually make it harder to modify or extend other parts that over time really do need to be modified or extended in some ways.
Generally speaking, i think you’re better off focusing on the following goals/principles:
- Avoid writing classes that are slutty
- Make sure that your consumers primarily communicate with your classes through interfaces. Though you don’t need to put everything behind an interface either… pretty much anything that people might need/want to change in some way typically are good candidates.
- Use Dependency Injection so implementations can easily be switched with others
- Use virtual methods unless you can think of a really good reason not to
- Don’t make your classes internal unless you can think of a good reason to do so (and keep in mind that we can still do whatever we want with them through reflection, and will do so if that turns out to be the best way to get something working the way we need it to if you failed to provide proper extension points)
- Do not seal classes unless you can think of a really good reason to do so
- Do not worry about performance up front, unless for those places where you are going out-of-process (remote services, databases, file systems, etc…)
- Keep your classes small and focused
- Learn about the SOLID principles, and apply them. Keep in mind that going for 100% SOLID code is typically not worth it either. Go for the 80/20 rule here.
If you keep those things in mind, you will typically end up with code that is flexible to use, and easy to change.
Obviously, all of this assumes that you are in a position where you can go back to the code and make changes. If you’re releasing frameworks/libraries/components that will be used by a lot of people and can’t afford to break backwards compatibility, then you probably need to be more strict about these things because then you can’t always just go back to change something. I don’t think it’s a stretch to claim that most developers are not in this situation however, so most of us often don’t need to waste time thinking about those things in advance
January 5th, 2010 at 1:30 am
[...] This post was mentioned on Twitter by Chris Nicola, Scott Muc. Scott Muc said: RT @lucisferre: Excellent post for new developers, wish I had read something like this when I was starting out: http://bit.ly/6WDZhN [...]
January 5th, 2010 at 5:26 am
From Mr Knuth’s “Computer Programming as an Art (1974)”
Premature optimization is the root of all evil (or at least most of it) in programming.
January 5th, 2010 at 7:31 am
Thanks for this great post! I think this is exactly what I needed to hear right now.
January 5th, 2010 at 9:54 am
Nice post. It confirms that I am on the right highway. Thank you.
PS: Do you accept suggestions for BPosts
January 5th, 2010 at 9:55 am
@Mynkow
definitely, though i don’t make promises
January 5th, 2010 at 12:56 pm
Well, it will be very interesting how experienced person like you will handle big, really big, project (ERP is pretty big for example). Yes, I know, DDD is handy. But do you start with some UML diagrams, some workflows because I don’t think TDD can handle at a whole such a project. Ofc at some point you will start doing it but not at the very beginning. I am not talking about the whole project life cycle but the starting. You must agree with me that the good start of a sprinter brings him the gold medal. So what is your architectural opinion
(Hope you got the point)
January 5th, 2010 at 1:17 pm
we never use UML, unless maybe some use-case diagrams if the customer really asks for it
if it’s a big system then i’d try to identify the large seperate blocks and try to keep them separated as much as possible… other than that, we just start implementing use cases within those blocks and try to avoid problems as much as we can
as for design: DDD could be useful in that situation, but not necessarily… if your really big project is just big but not very complex, then maybe DDD would be overkill… then again, maybe not. it always kinda depends.
as for the sprinter thing… a really big project is more like a marathon, starting out with a sprint is gonna make sure you’re going to have a hard time reaching that finish line
. The only thing you can do is try to make sure that your progress is always sustainable.
January 5th, 2010 at 1:54 pm
10x D.B. It is sad that your recent posts do not include HNibernate
January 13th, 2010 at 6:34 pm
However old I get, I will never stop having the urge to do this. You just learn to deal with it; as a young child learns not to say the first thing that comes into his head, a young coder eventually learns not to follow their first instinct.
My first proper job was as a coder on a computer game. My first task was to program the way that characters turned their heads. I decided to do it so that each eye’s direction was processed independently, because I was determined that the eyes would focus properly. But the game was so zoomed out that you never saw the difference between the eyes anyway. I didn’t last long as a computer game coder.