What self-respecting programmer doesn’t love a good pizza? The melty cheese, the zesty spices, the chewy crust — what’s not to love? Maybe you love it so much that you decide to make your own. You decide that nothing but the best will do, so you go all out. You grind the flour for your dough from organic wheat, cook up your own sauce from fresh tomatoes and herbs, make your own mozzarella cheese (yes, really), and add other fresh goodness from the local farmers market. You spend hours in the kitchen, tossing the dough by hand, spreading the sauce just right, and laying down layers of perfectly placed toppings and cheese. You wait an hour for the pizza stone to heat up in your oven, slide the pie into place, then after a few more minutes, finally, you bite into the first bite of pure pizza heaven.
But three slices later (or four, or five…) you realize that you have made more pizza than you can eat in a meal. So you wrap it up and tuck it into the fridge between the orange juice and that questionable carton of yogurt.
After a good night’s sleep, you’re famished and ready for the most important meal of the day. And, since cold pizza is the breakfast of champions, what do you do?
Naturally, you grab the wheat grinder and start making some flour, and turn on the stove to cook up some more sauce, right? No? Well of course you don’t. Because there is perfectly good pizza already made, right there in the refrigerator. If you insist, you can even warm it up a little first. There is no need to make an entire new pizza when you can just grab a slice of the one you already have.
So why on Earth do programmers insist on writing the same code over and over when they’ve already written it before? If code were pizza, there wouldn’t be room to walk without stepping on pepperoni, while the fridge is overflowing with pizza that isn’t being eaten!
One of the fundamental programming rules to live by is:
DRY: Don’t Repeat Yourself!
It doesn’t matter if you’re writing one quick little web page that you’ll only ever use once (or so you think at the time) or a huge client/server application, you should always break up your code into little manageable chunks related to the particular tasks your program needs to perform. Each of those little chunks becomes a function or method, and you then use those functions or methods to build your application.
Consider the following classic ASP snippet:
dim dbConnection set dbConnection = CreateObject("ADODB.Connection") dbConnection.ConnectionString = "UID=mydatabase;PWD=supersecret;driver=(Microsoft ODBC for Oracle);server=databoss;" set recordset = server.CreateObject("ADODB.RecordSet") sql = "select firstname, last name from customers" recordset.Open sql, dbConnection if recordset.eof = true then Response.Write("There are no customers! Quick! Call marketing!!") end else ' Print a big table of customers here end if
That’s all well and good and when you look at an ASP site, you will see code like that in lots of files. But that’s entirely the point! You shouldn’t see that in lots of files. You should see all of that except the sql statement and the result printing in precisely one file. By having it in multiple places, a maintenance nightmare is created. If the password changes, you have to update dozens of files and hope you don’t miss any. If you change databases, or drivers, or connection technology, you’ve got to change all those dozens of files.
Perhaps a better implementation would be something like this:
<!-- #include File="databaseStuff.asp" --> dbConnection = getDatabaseConnection() recordset = dbQuery(dbConnection, "select firstname, lastname from customers") if recordset.eof = true then Response.Write("There are no customers! Quick! Call marketing!!") end else ' Print a big table of customers here end if
Then in databaseStuff.asp:
function getDatabaseConnection() dim dbConnection set dbConnection = CreateObject("ADODB.Connection") dbConnection.ConnectionString = "UID=mydatabase;PWD=supersecret;driver=(Microsoft ODBC for Oracle);server=databoss;" end function function dbQuery(connection, sql) Dim recordset set recordset = server.CreateObject("ADODB.RecordSet") sql = "select firstname, last name from customers" dbQuery = recordset end function
Obviously this is vastly oversimplified, but you get the idea. Now you’ve isolated database connectivity to a single function in a single file, and you can include that file and call that function from anywhere in your application. If your database password or driver or server needs to change, you change it in one place and you’re done. You can choose whether your application will allow one connection or many connections by just changing this one file — the rest of your application can remain blissfully ignorant.
Likewise, the dbQuery method will handle the details of how record sets are created and retrieved, and you don’t need to clutter your application with those details. This particular example is weak for the sake of simplicity. In reality, you would want a function that would handle parameterizing the query, building argument collections, handling certain kinds of errors, and so on. But again, you could and should put the bulk of that into a method like this once and then use it throughout your application as needed.
Avoiding repetitive code saves work now, as well as down the road. The code becomes easier to maintain not only because there is less of it, but also because it’s easier to read and understand. This is important to you (because six months from now you won’t remember all the details of the code) and to your team and others who may inherit the project after you. So stop repeating yourself, and you may find time for more interesting pursuits.