A New PerspectiveThese articles are based on 14 years of experience, research, and lessons learned in the technology industry. Since getting back to writing code full-time (as of Jan 2012), I've learned and re-learned a lot of things and gained an entirely new understanding of others. I thought now would be a good time to start distilling knowledge and discussing concepts and theories, as I'm currently neck-deep in a multisite (portal-style) C#/ASP.NET 4.0 application backed by SQL Server 2008: MyClassEvaluation. I want to know what other developers and IT pros think of these ideas too. Leave a comment or contact me on Google+.
It's All About the FrameworkWhat are we really doing when we write software? How is the code I write today to access a database and manipulate the data on a web page any different from the code I wrote 10 years ago to access a different database and manipulate that data? When you think about it, other than taking advantage of new language features and framework libraries, the vast majority of the work we do as developers consists of repeating essentially the same tasks with slight variations.
In fact, it could almost be said that there are no new algorithms. Cryptography and cutting-edge research aside, the basic problems of analyzing, sorting and filtering have been solved many times over. In frameworks such as .NET, Java, even PHP, the breadth and depth of functionality built into the base libraries is staggering. Chances are, for any feature to implement or task to perform you can think of, the framework contains something that will get you about 90% of the way there. If not, a little searching will usually turn up a download to solve the problem (discounting licensing concerns for the sake of brevity). We ignore this axiom at our own peril. I have seen massive amounts of time wasted on custom code that could instead have leveraged classes built into the base .NET libraries.
This leads us to one of the rules that I always follow for software development:
Never re-invent the wheel. Always ask the question, "Has anyone solved this problem before?"
It may be tempting to build that socket server or caching mechanism from scratch, perhaps imagining benefits over framework libraries such as reduced complexity or better performance. But such implementations are usually a mistake, with consequences extending far beyond the time invested up front in writing the code. When we make our own wheels, we immediately introduce problems:
- More lines of code means more possible defects
- Maintenance becomes more costly
- Re-use becomes more difficult, as lesser-used code paths may hide broken compatibility
Think about this: if Microsoft engineers spent months or years to design, build and test a library that does almost everything you need, why would you throw away that effort just because it isn't exactly what you wanted? The key factor here is testing. I can trust that the framework libraries I use are generally bug-free and performant; can I afford to invest that much time and effort in making my own code as robust? Incidentally, there is a huge difference between what one wants and what one needs in software development, even more so than most other things in life. A library that meets my functional requirements (what I need) but doesn't provide a certain interface or accept certain method parameters that I am already using (what I want) is usually a sign that I am misusing something else, and may point to an architectural or design flaw.
Every language and framework I use was developed by people far more qualified than I to make decisions on what is necessary and useful. But because all software is designed by people, they may make decisions that I disagree with or have a hard time understanding. Despite the possible friction, one of the most important lessons I've learned in my career, and one of my strictest rules, is:
Never fight the framework. Use it as it was designed and intended to be used, whether you agree with/like it or not.
In ASP.NET, I've seen many cases where a developer reverted to familiar patterns to match their aesthetics and experience, such as rendering a table with static HTML as strings while iterating over a recordset. While this pattern works fine and may even execute faster than some options, it is a nightmare to maintain such code compared with simply using the built-in methods of databinding to a GridView. If your recordset does not facilitate that, it's time to rewrite the query or do some more advanced databinding. In the end, even if you dislike a certain programming paradigm that is used throughout a framework, you will definitely be able to appreciate it when you reap the benefits of sticking to that paradigm - notably less frustration and a simpler, more maintainable implementation.
Update (07/21/2012): I recently caught myself breaking this rule, where I persisted in using a custom base class and UserControl I had previously designed for handling data entry/edit forms. It had an event model to notify the form of an edit or update on its database object, but due to the rendering and event order of .NET controls and containers, I was unable to hook the form's events to a RadGrid (Telerik's specialized .NET GridView) for use as an edit form. I wasted several hours trying to work around the problem, and finally solved it (with less code) by just using the form template functionality built into the RadGrid and giving up on the custom class.