Thursday, June 14, 2012

Code# Concepts: Frameworks

A New Perspective

These 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 Framework

What 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:
  1. More lines of code means more possible defects
  2. Maintenance becomes more costly
  3. Re-use becomes more difficult, as lesser-used code paths may hide broken compatibility
If we maximize utilization of what the framework gives us for free, we write far less code to achieve the same or better results: the old "code less, do more" adage. I believe that one measure of software quality is the number of lines of code written to meet the requirement - and fewer is almost always better, for the reasons listed above and more.

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.

Wednesday, June 13, 2012

A Lesson in Shooting Yourself in the Foot

Sometimes I forget everything I know.Take a Fedora 14 configuration issue, for instance, on one of my old machines:

Last week Nautilus was suddenly unable to access special GVFS URIs like Computer: or Trash:. Kind of annoying, but not a killer bug. I forgot about it for a few days, until I plugged in a USB drive and found that it didn't automount. Okay, more annoying, I thought, and probably related to the same GVFS error. Time for some Googling on the error message.


The first page of results revealed the problem to be my own fault: I had compiled Anjuta from source, and it required updated versions of GIO and GVFS, among many other GTK+ libraries. Unthinking, I had installed Anjuta and the new libs in /usr/local. Everything ran fine, until I rebooted. I only do that once every few months on this machine, and the error seemed disconnected from my earlier actions by that time. Once I read the post, it made sense. I renamed /usr/local/lib, lib64, etc. and rebooted, and Nautilus was fixed.

So this hiccup sparked a conversation between myself and a colleague: Why was it so easy to screw my system up, and what could or should be done about it? This, of course, led to a more philosophical discussion on the nature of an open-source OS, general UNIX principles inherent in Linux, and the ability to have one's cake and eat it too.

The basic UNIX principle that I failed to acknowledge when installing a new GIO/GVFS was the multi-user system. Users can be either remote (network) or local (physically at the console), and the system loads libraries, determines paths, and does a lot of other setup depending on what kind of user you are. This is accomplished by a simple but ingenious mechanism: directory inheritance and overriding via the PATH environment variable.

This principle extends to the individual user, where files or directories starting with "." in a home directory (~) can override system or local defaults. It is one of the features that makes Linux so infinitely customizable, meshing perfectly with the open-source philosophy. But it bit me this time; judging by the search for my simple error, this same oversight has bitten other users for years and will likely continue to do so. With root/administrative privileges, it is very easy to render any OS unstable or unbootable. I've seen more than one instance of users accidentally deleting their Windows system directories. Everything works until they reboot...

In a nutshell, the lesson learned is that almost any user installation or customization can and should be performed without root privileges. I should have installed the new libraries, and possibly Anjuta, in my home directory by specifying the prefix and libdirs during the build. So should anything be done to protect me from shooting myself in the foot again? Probably not. When one is constantly installing new development libraries and dependencies to hack and build the latest shiny version of application foo, some of them will inevitably conflict with application bar. The safest way to install, of course, is to use only the distribution's package manager and repositories. However, I can envision SELinux providing an extra layer of security by protecting distribution files.