taming the legacy beast: turning wild old code into a sleak new thoroughbread

Post on 14-Jun-2015

264 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

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.

TRANSCRIPT

Taming the Legacy Beast

Turning wild old code into a sleek new thoroughbred.

Chris LaningSenior Web DeveloperFineStationery.com

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.

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?

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

R.U.N.

● Read

● Understand

● Notate

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

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.

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.

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.

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”.

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.

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!

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

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.

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.

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”.

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

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.

Group Functionality

● Use comments to put separators between similar lines of functionality.

● Examples: o Lines pulling values from the URL or Form scopeso Lines that build the page title using various pieces of

data.o Lines that query or update the database.

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.

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.

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

Components (CFCs)

● At its simplest, a CFC is a collection of functions.

● These functions can be called from other CF pages.

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.

Before (CFM page)

<!--- Pull Product values from FORM info. ---> <cfset productId = form.productId> <cfset brandId = form.brandId> <cfset productAttributes = ListToArray(form.prodAttributes)> <cfset quantity = form.quantity> <cfif quantity LTE 10> <cfset orderSize = “small”> <cfelseif quantity LTE 50> <cfset orderSize = “medium”> <cfelse> <cfset orderSize = “Large”> </cfif>

Rewritten in Helper App (tag)

<cffunction name = “getProductValuesFromForm” returnType=”struct” access=”public”>

<cfargument name="argForm" type="struct" required="true">

<!--- Create return structure ---> <cfset oProductVals = structNew()>

<!--- Pull Values from the Form data ---><cfset oProductVals.productId = argForm.productId><cfset oProductVals.brandId = argForm.brandId><cfset oProductVals.attributes = ListToArray(argForm.prodAttributes)>

<cfset oProductVals.quantity = argForm.quantity>

Rewritten in Helper App (tag) (cont.)

<cfif oProductVals.quantity LTE 10> <cfset oProductVals.orderSize= “small”>

<cfelseif oProductVals.quantity LTE 50> <cfset oProductVals.orderSize= “medium”>

<cfelse> <cfset oProductVals.orderSize= “Large”>

</cfif> <!--- Return Structure ---> <cfreturn oProductVals></cffunction>

Rewritten in Helper App (script)

function getProductValuesFromForm (argForm) {

// Create structure for the product valuesoProductVals = {};

// Pull values from the form data. oProductVals.productId = argForm.productId;

oProductVals.brandId = argForm.brandId; oProductVals.attributes =

ListToArray(argForm.prodAttributes);oProductVals.quantity = argForm.quantity;

Rewritten in Helper App (script) (cont.)

// Set order size based on the quantityif (quantity LTE 10)

{oProductVals.orderSize = “small”;}else if (quantity LTE 50)

{oProductVals.orderSize = “medium”;} else

{oProductVals.orderSize = “Large”;}

// Return Product Valuesreturn oProductVals; }

After (CFM page)

<!--- Load Helper Apps ---><cfset oProductHelper =

createObject(“component”, “ProductHelper”>

<!--- Pull Product values from FORM info. ---> <cfset oProductVals =

oProductHelper.getProductValuesFromForm(form)>

WAIT!!!!

All you did was move code from one file to another. In fact, overall, there is

actually MORE code!

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.

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.

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.

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

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.

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.

Identify Your Objects

● Look at your application and first identify the obvious objects like user, product, category, etc.

● Create object CFCs for each one.

Object CFC (tag style)

<cfcomponent displayname=”category” accessors=”true”>

<!--- Define the properties ---> <cfproperty name=”id” required=”true” type="numeric"

getter=”true” setter=”true”hint="The category id.">

<cfproperty name=”name” required=”true” type="string"

getter=”true” setter=”true” hint="The category name.">

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>

Object CFC (script style)

component displayname=”category” accessors=”true” {

// ******************** PROPERTIES ********************property name=”id” required=”true” type="numeric"

getter=”true” setter=”true” hint="The category id.”;

property name=”name” required=”true” type="string" getter=”true” setter=”true” hint="The category name.";

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 valuereturn strCatName;

}}

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”!

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.

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.

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>

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>

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();

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; }}

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.

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 easiero 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.

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.

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.

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!

6 Step Process

1. Run it.2. Divide it.3. Group it.4. Subtract it.5. Orient it.6. Square it.

Model View Controller

● 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.

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!

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”.

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.

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.

MVC Frameworks

● Many of them also include or use dependency managers which make managing your objects and services much easier and cleaner.

Model View Controller Frameworks

● CF MVC Frameworks (examples): o FW/1o Coldboxo Fuseboxo FarCry

Moving to MVC Framework

● Process depends on the framework you choose.

● But you have done the hard part:o Organized your codeo Separated views and modelo Created objects and services.

So we started with a long complicated legacy app.

We read it.We figured it out.

We carefully documented what we figured out.

We moved all the display code to the bottom of the file.

We moved our static variables and constants to the top and eliminated any duplicates.

We grouped similar variables and constants together and looked for opportunities to

organize them into structures.

We grouped lines of similar functionality together.

We moved each of those groups of similar lines into functions in a helper CFC.

We replaced those similar lines of functionality with calls to their respective function in

the helper CFC.

That made our initial file smaller and made each of

those functions reusable and testable.

We looked at our application and identified the logical

“objects” we found.

We created object CFCs complete with properties and methods defined for each of

these objects.

We moved the functions from our helper app that worked on

these objects into their respective object CFC.

Those “functions” magically became “methods’!

We looked at the methods of each of our object CFCs.

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.

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.

Back in our original file we made sure all the function

calls were now pointing to the appropriate objects and

methods.

We then considered the entire request one big object and added our other objects as

properties of that one.

Which allowed us to keep all of our data organized and made debugging a breeze since we only had to dump

one object.

Which allowed us to keep all of our data organized and made debugging a breeze since we only had to dump

one object.

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”.

We moved the display code from the bottom of our original

file into its own file which made it a basic “view”.

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.

Which leaves us in a great position to migrate into the

MVC framework of our choice!

Or just leave it as is. After all, we have pretty well

tamed that wild old code!

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”)

JSDoc

Example

/* *************************************************************************** * 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. */ */

JSDOC

OUTPUT

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.

/** * 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

**/

CF Component Viewer Output

top related