views:

790

answers:

7

One thing that's really been making life difficult in getting up to speed on the codebase on an ASP classic project is that the include file situation is kind of a mess. I sometimes find the function I was looking for being included in an include file that is totally unrelated. Does anyone have any advice on how to refactor this such that one can more easily tell where a function is if they need to find it?

EDIT: One thing I forgot to ask: does vbscript have any kind of mechanism for preventing a file from being included twice? Sorta like #ifndef's from C?

A: 

i think you should consider moving your code from ASP VBScript to Visual Basic COM DLLs. that'll ease on you having too much includes.

cruizer
why the negative votes? COM components would cut down on includes and provide a path for .NET migration?
jwmiller5
+11  A: 

There are a few basic things you can do when taking over a classic ASP application, but you will probably end up regretting doing them.

  1. Eliminate duplicate include files. Every classic ASP app I've ever seen has had 5 "login.asp" pages and 7 "datepicker.js" files and so forth. Hunt down and remove all the duplicates, and then change references in the rest of the app as necessary. Be careful to do a diff check on each file as you remove it - often the duplicated files have slight differences because the original author copied it and then changed just the copy. This is a great thing for Evolution, but not so much for code.
  2. Create a rational folder structure and move all the files into it. This one is obvious, but it's the one you will most regret doing. Whether the links in the application are relative or absolute, you'll have to change most of them.
  3. Combine all of your include files into one big file. You can then re-order all the functions logically and break them up into separate, sensibly-named files. You'll then have to go through the app page by page and figure out what the include statements on each page need to be (or stick with the one file, and just include it on every page - I can't remember whether or not that's a good idea in ASP). I can't comprehend the pain level involved here, and that's assuming that the existing include files don't make heavy use of same-named globals.

I wouldn't do any of this. To paraphrase Steve Yegge (I think), "there's nothing wrong with a classic ASP application that can't be fixed with a total rewrite". I'm very serious about this - I don't think there's a bigger waste of a programmer's time in this world than maintaining an ASP app, and the problem just gets worse as ASP gets more and more out of date.

MusiGenesis
I wish that were an option for me. :(Unfortunately, I don't have a lot of choice.
Jason Baker
If the app is working (to whatever level of success required), then I'd say minimize your pain and change nothing unless you absolutely have to. I've done this a lot, and refactoring ASP is pretty much never worth it.
MusiGenesis
I have two classic ASP apps that I wrote 10 years ago that I supposedly still support. But I refuse to go into the code and do much. Its a waste of time. I finally got the word last week that they are scheduled for destruction in April. :)
tyndall
+1 just for the evolution analogy, that's just brilliant
MarkJ
+1  A: 

How about this: refactoring ASP is like climbing Mt. Everest. It seems like a fun and exciting idea until you're stuck in a blinding snowstorm at 28,000 feet, out of oxygen and using the last bit of power in your radio to tell your wife you love her.

MusiGenesis
For me ASP refactoring is more like Dante's Divine Comedy :)
Kev
I think the difference is that Dante knew they were going to let him out at the end.
MusiGenesis
+9  A: 

@MusiGenisis bullet point list is good advice to follow but I'd disagree with -

"I wouldn't do any of this. To paraphrase Steve Yegge (I think), "there's nothing wrong with a classic ASP application that can't be fixed with a total rewrite". I'm very serious about this - I don't think there's a bigger waste of a programmer's time in this world than maintaining an ASP app, and the problem just gets worse as ASP gets more and more out of date."

All very well, but if it's a sizable legacy app doing complete re-writes is often not possible due to a lack of developer time/resource.

We have a fairly large classic ASP app which has grown arms and legs over the years, it's not pretty but it does serve the business needs. We have no time to spend the next six months doing a complete re-write, it would be nice, but just not possible. Our approach is -

  1. Where there's new functionality required, it's implemented in ASP.NET. This happens 95% of the time. The 5% edge cases usually being that there are a large number of points where the new app code touches the old app requiring us to do a lot of classic ASP re-work potentially making the app more fragile.

  2. Where there's a change in functionality we assess whether we can refactor to ASP.NET with minimal impact. If this isn't possible then we'll implement the change in classic ASP and tidy up existing code as we go along e.g. simplifying include file nesting, replacing javascript with more cross browser friendly code, that kinda thing.

In answer to your question about #ifndef's, there isn't an equivalent I'm afraid.

HTH
Kev

Kev
If you're stuck with it, I think your approach is perfectly sensible.
MusiGenesis
+1 Martin Fowler calls this a "strangler application". It's pragmatic, incremental and it delivers good customer value. http://martinfowler.com/bliki/StranglerApplication.html
MarkJ
@Mark - thanks for the linky, I wondered if that approach had a name.
Kev
+1  A: 

Wow. It constantly surprises me how many people have a hate for ASP. In decent hands it's a perfectly capable language for designing web applications.

However, I will concede that the way include files are managed in ASP can be a bit of a brainache -- because (depending on how you use them) they have to be loaded and parsed even if you're not using half the functions contained within.

I tend to have one include file (initialise.asp or some such) that itself includes links to several functions libraries (lib_http.asp, lib_mssql.asp or similar) and all library functions are self-contained so there is no worry about crossing variables. Any global vars are declared and set in the master file. This means I can use a function anywhere, any time and not worry about where it was defined, it's just there for use. And IDEs such as Visual Studio and Primalscript have the ability to "jump to definition" when you find a call to a function that you don't recognise.

Then, any script-specific includes are included in the script after the call to this master include file.

I concede that this is a memory-hungry approach as all the functions in all the libraries are compiled for every script call, so the method needs refining for each site you develop -- decide what to call via the master include and what is more page-specific. It would be nice to be able to only load what you need -- but that's the DLL approach and is not available for the majority of real-world developments, and also you'd have to weigh up the processor cost of compiling small scripts vs loading components.

A concise directory structure is requisite and easily developed, but it can be a chore to wade through all the code in an existing site and change any links or mappath calls. Also, be aware that some IIS administrators disallow the '..\' method of traversing directories via VBScript, so then all file references have to be absolute paths.

Cirieno
+1  A: 
  1. Use one file to global headings and includes (lets name it t-head.asp). This file is included in all asp files.
  2. Use one file to make the site visual global header (logos, menus, etc) and include it right behind . Let call it t-begin.asp
  3. Use one file to make the site visual global footer (copyright, google analytics, etc.) and closing all divs or tables opened in t-begin.asp. Lets call this file t-end.asp
  4. Use one folder to put the business logic files, called BUS. The files in this folder can not have includes. Every function inside the file must be preceded by the name of the logic unit (IE: all function in products.asp must begin with product_*)
  5. Use one folder to put some reused UI code called UI. The files in this folder can not have includes.

Example:

<%@  Language=VBScript %>
<% Option Explicit %>
<% Response.Buffer = true%>
<html>
<head>
<!--#include file="../general/t-head.asp"-->
<!--#include file="../bus/product.asp"-->
<title>Products page</title>
</head>
<body>
<!--#include file="../general/t-begin.asp"-->

   <% 'all your code  %>

<!--#include file="../general/t-end.asp"--> 
</body>
</html>
Eduardo Molteni
A: 

I don't know of a way to prevent a double inclusion, other than getting an error message that is. Are you seeing includes placed throughout the page, which is making them difficult to spot?

Just as an aside, are you working with a copy of the code and the database on a development server? From my experience, the first thing to do is separate yourself from the live site ASAP. While a hassle initially, it'll give you the freedom to make changes without messing up the live site. It's easy to make that one tiny change in an include and BAM! the whole site goes down.

I've worked through a few projects like you've described and used the following strategies:

Complete rewrite - perfect when there's time/money, but usually I get the call when something has gone wrong and results are needed ASAP.

Smaller projects - I open up everything in the IDE and just start searching all the project files for the functions/sub, in order to build a knowledge of the include logic. Pretty much each time, everything is spread out everywhere, so I start rebuilding the includes organized by business logic. I've also run across inline code (raw code, not subs or functions) thrown into an include, so I'll usually just pull the code back into the page for refactoring later.

Larger projects - I'll use some code I have laying around to parse the includes for lines with sub/function headers and dump those to a text file to build up a list of what routines are where and refer to that. This comes in handy when you've got a ton of includes on each page and can't get your head around the codebase.