Got a legacy application? Trying to turn into a modern one? This presentation, given by Chris Laning, takes you through a methodical process that helps you attack that seemingly insurmountable task and tame it like a pro! The presentation is ColdFusion focused, but many of the methods employed could be used by programmers in other languages. This presentation was given at NCDevCon on September 13, 2014 in Raleigh, NC.
Chris is a Senior WebDeveloper and has been doing web development since 1996.
Presentation on how to chat with PDF using ChatGPT code interpreter
Taming the Legacy Beast: Turning wild old code into a sleak new thoroughbread.
1. Taming the Legacy Beast
Turning wild old code
into a sleek new thoroughbred.
Chris Laning
Senior Web Developer
FineStationery.com
2. My Background
● Web Developer since 1996 (CF since 1998)
● Senior Web Developer with FineStationery.com
● Current Producer of CF Hour Podcast
(http://www.cfhour.com)
● Other roles: Dad, voice-over artist, audio editor, theatre
guy, Disney geek, NASCAR fan, and political junkie.
3. Case Study Background
● Legacy App (originally written in 1999)
● Maintained and updated but some sections original
● Mix of procedural, OOP, includes, custom tags,
components and “old-school” tagging.
● Very complicated business rules
● My first big assignment was tackling the search page
portion of the app which is responsible for over 70% of
site traffic.
● Main page had thousands of lines of code.
● Where to begin?
4. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
6. READ THE CODE
● Only real way to learn it
● Familiarizes you with the style(s) being used
● Helps you get a feel for the flow of the
application
● Gives you a sense of the enormity and
complexity of the application
7. Don’t Just Read. Understand!
● Make sure you know what each line is doing.
● If not, do your homework.
● Go down the “Rabbit Hole”. Jump out to
included files, custom tags, invoked CFCs,
and external APIs. Make sure you
understand them as well.
● Trace some sample values through the code
to help you understand.
8. Understand? Good! Now Document!
● You’ve done all the hard work! Why do it
again?
● In a really complicated application you can
help keep track of where you are.
9. Documentation Tips
● Add space and section markers to make it
read better (less overwhelming).
● Summarize each section in your own words,
as you understand it.
● Be verbose! Detailed comments, while time
consuming now, will save you (or anyone
else), tons of time in the long run.
10. Documentation Tips
● Try to be as non-technical as possible.
● Add comments below existing comments,
again in your own words.
● Format comment blocks in a consumable
format like JDocs.
● Don’t forget to document trips “down the
rabbit hole”.
11. Documentation Caveats
● If you are not the “owner” of the code base,
ask your supervisor or client if they mind you
adding the comments. Chances are they will
welcome it.
● If you are not allowed to add comments, do
so in your local copy. Make sure to strip
them out before committing.
12. So at this point we….
● Understand the application.
● Understand the flow.
● Have a feel for the coding style.
● Have a better sense of the scope of the
application.
● Have verbose documentation of the app…in
a writing style PERFECT for you!
13. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
14. Separate “Form” From “Function”
● Draw a line in a comment at the bottom of
the page.
● Go through code. Move any display code
(HTML) to below the line.
● If your display code includes computed
values, put them into a variable. Move the
display code and reference the variable.
15. Separate “Form” From “Function
● Your display section should have little to no
decision and looping logic
● Only have loops that are used to generate
additional HTML (or other display code)
● Only have if statements that determine
whether or not to display given HTML code.
16. So at this point we….
● Understand the application, scope coding
style and flow.
● Have fully documented the app and process.
● Separated all of the “business logic” from the
“display logic”.
17. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
18. Group Variables
● Move all static variable definitions (variables
that don’t change during the request) to the
top.
● Can expose duplicate variables you can
consolidate.
● Group similar variables and look for
opportunities to combine into structs for
easier management.
19. Group Functionality
● Use comments to put separators between
similar lines of functionality.
● Examples:
o Lines pulling values from the URL or Form scopes
o Lines that build the page title using various pieces of
data.
o Lines that query or update the database.
20. Group Functionality
● Unless order is a factor, move similar groups
of functionality together
● Examples:
o Pull all groups that build or update the title together.
o Pull all groups that update user information together.
21. So at this point we….
● Understand the application, scope, coding
style and flow.
● Have fully documented the app and process.
● Separated all of the “business logic” from the
“display logic”.
● Grouped and arranged our business logic
into small segments.
22. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
23. Components (CFCs)
● At its simplest, a CFC is a collection of
functions.
● These functions can be called from other CF
pages.
24. Move Code Groups Into “Helper” App
● Create an empty Component (CFC) and
name it “(your app name)Helper”. Example:
“searchHelper.cfc”
● If a code group is more than 2 or 3 lines,
move it to the Helper CFC.
● Wrap that code in a function.
28. Rewritten in Helper App (script)
function getProductValuesFromForm (argForm) {
// Create structure for the product values
oProductVals = {};
// Pull values from the form data.
oProductVals.productId = argForm.productId;
oProductVals.brandId = argForm.brandId;
oProductVals.attributes =
ListToArray(argForm.prodAttributes);
oProductVals.quantity = argForm.quantity;
29. Rewritten in Helper App (script) (cont.)
// Set order size based on the quantity
if (quantity LTE 10)
{oProductVals.orderSize = “small”;}
else if (quantity LTE 50)
{oProductVals.orderSize = “medium”;}
else
{oProductVals.orderSize = “Large”;}
// Return Product Values
return oProductVals; }
30. After (CFM page)
<!--- Load Helper Apps --->
<cfset oProductHelper =
createObject(“component”, “ProductHelper”>
<!--- Pull Product values from FORM info. --->
<cfset oProductVals =
oProductHelper.getProductValuesFromForm(form)>
31. WAIT!!!!
All you did was move code from one file
to another. In fact, overall, there is
actually MORE code!
32. Yes…..but we also…...
1. Made initial file more readable and smaller.
2. We made our code reusable.
a. We can call this CFC from other pages that need the
same processes done.
3. We made our code testable.
a. Since it is a function we can run unit tests on that
specific process.
b. Unit testing helps with development and debugging.
33. So at this point we….
● Understand the application, scope, coding
style and flow.
● Have fully documented the app and process.
● Separated all of the “business logic” from the
“display logic”.
● Grouped and arranged our business logic
into small segments.
34. So at this point we….
● Have shrunk the size of our original file.
● Have turned the small segments of code into
reusable, and testable functions.
35. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
36. Object: What is it? (In plain terms)
● A way of thinking about the data in your application as
“nouns”. Example: user, category, product.
● These objects have two important parts: properties and
methods.
37. Properties and Methods
● Properties are things that describe the object
(“adjectives”). Example: User has last name, first name,
id, email address, etc.
● Methods are functions that operate on or with the the
object and its properties (“verbs”). Example: You could
have a getFullName() method on the user object that
grabs the first name, last name, and suffix of the user
and returns them in a concatenated string.
38. Identify Your Objects
● Look at your application and first identify the
obvious objects like user, product, category,
etc.
● Create object CFCs for each one.
40. Object CFC (tag style) (cont.)
<!--- Define the Methods --->
<cffunction name = "getCatName" access=”public”
description=”Returns the category name”
returnType=”string”>
<!--- Call implicit getter to get the value of the name property --->
<cfset strCatName = this.getName()>
<!--- Return the value --->
<cfreturn strCatName >
</cffunction>
</cfcomponent>
42. Object CFC (script style) (cont.)
// **************** PUBLIC METHODS ****************
public function getCatName(){
// Call implicit getter to get the value of the name
strCatName = this.getName();
// Return the value
return strCatName;
}
}
43. Move Helper Functions
● Look through the helper cfc and see if any functions
work directly with and exclusively on an object.
● Move those functions to their respective object CFCs.
● Hey! Those “functions” are now “methods”!
44. Services
● Services are CFCs that provide additional functionality
around objects.
● Basically, methods that do anything other than read,
modify, or perform an action on the properties of a
single instance of the object should be moved to a
service cfc.
45. Examples of Service Methods
● The method persists (reads, updates or stores) the
object in a database or file.
● The method returns an array of more than one instance
of the object. For example, returns a list of all active
categories.
● The method interacts with other objects. For example, a
product method that returns the information on the
category its associated with.
46. Service CFC Example (tag style)
<cfcomponent displayname=”categoryservice”>
<cffunction name = "getCategories" access=”public”
description=”Returns a list of category objects”
returnType=”array”>
<!--- Create a blank array to store the categories in --->
<cfset aryCategories = []>
<!--- Pull list of categories from the database --->
<cfquery datasource=”mydsn” name=”qryCategories”>
SELECT id,name FROM Category
</cfquery>
47. Service CFC Example (tag style) (cont.)
<!--- Loop through the query, create objects, and add to array --->
<cfloop query=”qryCategories”>
<cfset oCategory = createObject(“component”,”category”)>
<cfset oCategory.setId(id)>
<cfset oCategory.setName(name)>
<cfset ArrayAppend(aryCategories, oCategory)>
</cfloop>
<!--- Return the value --->
<cfreturn aryCategories>
</cffunction>
</cfcomponent>
48. Service CFC Example (script style)
component displayname=”categoryservice” {
// ***************** PUBLIC METHODS *****************
public function getCategories(){
// Create a blank array to store the categories in
var aryCategories = [];
// Pull list of categories from the database
queryService = new query();
queryService.setDatasource("mydsn");
queryService.setName("qryCategories");
result = queryService.execute(
sql="SELECT id,name FROM Category");
qryCategories = result.getResult();
49. Service CFC Example (script style) (cont.)
// Loop through the query, create objects, and add to array
for (var intX = 1; intX lte qryCategories.recordcount;
intX = intX + 1)
{oCategory = createObject(“component”,”category”);
oCategory.setId(qryCategories[“id”][intX]);
oCategory.setName(qryCategories[“name”][intX]);
arrayAppend(aryCategories, oCategory);
}
// Return the value
return aryCategories;
}
}
50. One Last Object
Now that we have several smaller objects on
our page we can store all of them as properties
of an object that represents the entire request
cycle. So if this was a search page, we could
create a search object with properties like
category, products, etc.
51. Why One Big Object?
● It helps you organize your data better.
o The properties should be the objects that make up
the page. For example: userInfo, categoryInfo,
URLInfo.
● Makes debugging easier
o Just dump out the main object and you can see
pretty much all the important info you need to see.
o Dump it in several spots and you can track the
progression.
52. So at this point we….
● Understand the application, scope, coding
style and flow.
● Have fully documented the app and process.
● Separated all of the “business logic” from the
“display logic”.
● Grouped and arranged our business logic
into small segments.
53. So at this point we….
● Have shrunk the size of our original file.
● Have turned the small segments of code into
reusable, and testable functions.
● Have created objects and services and
moved the helper functions to the
appropriate CFCs.
54. So at this point we….
● Have wrapped all of our important data up
into a single object we can easily debug
with.
● Are object-oriented now!
55. 6 Step Process
1. Run it.
2. Divide it.
3. Group it.
4. Subtract it.
5. Orient it.
6. Square it.
56. Model ViewController
● Model: The business rules, data, and
processes for the application.
● View: How that information is displayed or
returned.
● Controller: “Traffic Cop”. Routes the
request to the right processing methods and
the right layouts/views.
57. Surprise...you already have a model!
The object we created for the entire request is
really the model. It holds (or has the ability to hold)
all the data and values we need to know for the
request, as well as all those methods we created
to create, modify, or delete the data and values.
Its our model!
58. Surprise....you already have a view!
Take all the HTML and display code we moved to
the bottom of the original document and put it into
its own new document. Just make sure the
dynamic values it has are pointed to the
corresponding ones on the “model”.
59. Surprise....you ALMOST have a controller!
Whatever code you had left at the top of page,
should be pointed to the object methods, and is
therefore making changes to the model. Just add
some code at the bottom to load or execute the
view and you now have a basic MVC application.
60. MVC Frameworks
● There are several frameworks out there that
employ the MVC architecture. Evaluate them
and pick the one that works for you.
● Many favor “convention over configuration”.
That means that folder names and file
names follow conventions that help the
framework find them and know what they do.
61. MVC Frameworks
● Many of them also include or use
dependency managers which make
managing your objects and services much
easier and cleaner.
62. Model View Controller Frameworks
● CF MVC Frameworks (examples):
o FW/1
o Coldbox
o Fusebox
o FarCry
63. Moving to MVC Framework
● Process depends on the framework you
choose.
● But you have done the hard part:
o Organized your code
o Separated views and model
o Created objects and services.
77. We looked at the methods of
each of our object CFCs.
78. If the method did anything
other than read, modify, or
perform an action on the
properties of a single instance
of the object, we moved it to a
service CFC.
79. If the method did anything
other than read, modify, or
perform an action on the
properties of a single instance
of the object, we moved it to a
service CFC.
80. Back in our original file we
made sure all the function
calls were now pointing to the
appropriate objects and
methods.
81. We then considered the entire
request one big object and
added our other objects as
properties of that one.
82. Which allowed us to keep all
of our data organized and
made debugging a breeze
since we only had to dump
one object.
83. Which allowed us to keep all
of our data organized and
made debugging a breeze
since we only had to dump
one object.
84. Now that all of our data and
associated methods were on
one object, we found that we
had a “model” and were
nearly ready for “MVC”.
85. We moved the display code
from the bottom of our original
file into its own file which
made it a basic “view”.
86. All that was left in our original
files was code that called
various methods to build our
“model”. So we had our basic
“controller” too.
87. Which leaves us in a great
position to migrate into the
MVC framework of our choice!
88. Or just leave it as is. After
all, we have pretty well
tamed that wild old code!
89.
90. JavaDoc
● HTML documentation generator originally
designed by Oracle for Java.
● Starts with “/**” ends with “*/”.
● HTML Comments are passed through.
● Power is in the “@” delimited items
(example: “@author”, “@return”)
91. J
S
D
o
c
E
x
a
m
p
l
e
/* ***************************************************************************
* METHOD: _changeAlignment
* PURPOSE: Changes the alignment information for an individual line
* ARGUMENTS:
* argValue (string) - The value of the change. (Values: "l" (left), "c" (center), "r" (right))
* argID (string) - The id of the line we are working with.
* RETURNS: (Boolean) Flag indicating success
* PROCESS:
* 1. Compute line number.
* 2. Set status flag.
* 3. Ensure that the value is an integer.
* 4. Build the DOM IDs.
* 5. Change the alignment in the line information.
* 6. Update information on display.
* 7. Set status flag to true.
* 8. Return status.
* ------------------------------------------------------------------------------------------- */
/** JSDOC INFO:
* @access private
* @param {string} argValue - The value of the change. (Values: "l" (left), "c" (center), "r" (right))
* @param {string} argID - The id of the line we are working with.
* @desc <p>Changes the alignment information for an individual line.</p>
* @example
* // Change line alignment to "left"
* _changeAlignment("l","line1_1");
* // Change line alignment to "center"
* _changeAlignment("c","line1_1");
* // Change line alignment to "right"
* _changeAlignment("r","line1_1");
* @returns {boolean} Returns boolean flag indicating success. */
*/
93. CF Component Browser
● Accessed at:
http://(wwwroot)/CFIDE/componentutils/componentdoc.cfm
● Consumes some of the JDoc format.
● Everything between “/**” and the first “@”
param will be copied in its entirety. (HTML
formatted).
● @ + argument name + space + description
will populate description in the argument list.
94. /**
* METHOD: _getURLSpaceDecodedFormat()<br/>
* PURPOSE: Parse the pretty path and generate a structure of data needed by the search engine.<br/>
* RETURNS: SearchResults. <br/>
* PROCESS:<pre><ol><li>
* Initialize local variables.<li>
* Make working copy of original string.<li>
* Determine the length of the string to be decoded.<li>
* If the string has an extension (.html, .cfm, etc.) do the following:<ol type="A"><li>
* Store the extension.<li>
* Remove the extenSion from the working string.</ol><li>
* Loop through the string and do the following:<ol><li>
* Get the current segment.<li>
* Find the last underscore segment.<li>
* Convert all underscores to spaces.<li>
* If the last segment is a refinement identifier then preserve the underscore.<li>
* Replace segment with converted segment.</ol><li>
* Add stored extension back to the working string.<li>
* Return converted string</ol></pre>
* @output true
* @argOrigString The string we are decoding
* @param returnType String
**/