Recently in web development Category

HTML5, Round 2: Semantic Markup

The semantic web is an ever-evolving development to define the meaning of content viewed on the web. It is the responsibility of an HTML developer to build web pages with a sharp understanding of how the front-end markup should be structured. With the upcoming release of HTML5, new tags will be introduced to the language that will enable more fluid communication between the web and the people and machines that use it.

The common practice of laying out HTML markup these days is the use of the <div> tag, using id or class attributes to block out content. New tags included in HTML5 will display a much more readable layout when defining areas of a web page. Some of the important tags in this group are <section>, <aside>, <header>, <footer>, and <nav>. It is likely that most of these tags clearly state what they are doing, but each still deserves it's own explanation:

  • <section>
    This tag will be used to define generic content within a document. While id and class attributes will still be needed, this will still lay out a cleaner mode of blocking content than the now-standard <div> tag, thought the latter tag will not be deprecated and can still be used.
  • <aside>
    Sidebar content will be separated using this tag to show content that is "to the side" of main content areas. Generally this content is partially related to the primary focus of a given web page.
  • <header> and <footer>
    The topmost and bottommost blocks of content are often referred to as the "header" and "footer." Naturally, HTML5 introduces tags that clearly define this. It is likely that id and class attributes will no longer be needed with the use of the tags, unless a developer opts to use the <header> tag inside of a <section> tag.
  • <nav>
    Navigation menus will have their own tag in the markup, however it is uncommon for modern websites to have less than three nav menus on a single page, so id and class attributes would still be needed.

Below is a screenshot of how this code might look. The new tags form a much more readable structure to the markup of the page. It creates a much cleaner view and gives the viewer a better of understanding of what is being displayed.

semantic_screenshot.jpg

View a live example (it may not be the prettiest site, but it's more for the markup than anything)

One of the most beautiful aspects of the above examples is the HTML Document Type Declaration, or DOCTYPE. It is the absolute first statement in every single web page, and is used to trigger standards mode in a browser and then determines what iteration of the language is in use. In earlier versions, there was specific data required as part of the declaration, which may look something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

Earlier versions of HTML were based on SGML, Standard Generalized Markup Language, but HTML5 is not and therefore has no need for the extra junk in the DTD. Here is the new one:

<!DOCTYPE html>

As previously stated - beautiful.

There are a few other new tags that are still worthy of mention: <article> to block content for blog entries or news articles, <dialog> used for marking up a conversation, and <figure> to be used when associating a caption with embedded content (audio or video).

It is debatable whether these new standards should be used, considering that full cross-browser support for HTML5 is not there yet. Firefox, Safari, Chrome, Opera, and a few smaller browsers will support most of these new tags, but older versions of Internet Explorer will not. Allegedly, there is a way to use JavaScript to force Internet Explorer to acknowledge that the new tags exist, but if a user has JavaScript disabled in their browser, the page will not render properly. Given that IE6 and IE7 still have a large-enough share on the browser market, this hack method does not seem sound enough to use in common markup yet.

The direction the semantic web is going will no doubt enhance the overall user experience of the web, even if the user is not consciously aware of it. This markup overhaul will create a better environment to display content and has the potential to make web pages even just slightly faster and more scalable. These are all good things.



Labels: , ,

Web 3.0 - The semantic web - It all makes sense now

rdfa.jpgThere has been a lot of talk about Microformats, RDFa (Resource Description Framework - in - attributes) and Web Ontology Language (OWL). These naming conventions, although not widely used yet, present a solution that narrows down relevant search results and data correlation on websites.

As some of you may know, our favorite web pioneer has recently announced Google Search Options.

This refined view of data exploits semantic markup conventions making it easy for people to find the data they want. They also have some good documentation on structured data in their Webmasters guidelines section.

So far, it seems like the three most common implementations for rich snippets are reviews, people and products. When coding, property association for data can be added either by naming the element class or adding a property attribute. These two Firefox addons, Operator and Semantic Radar, will display this information.

Here is the recording from the webinar we attended today on the Semantic Web.



Labels: , , , ,

Tip: Using Procs with Thoughtbot's Paperclip

Thoughtbot's Paperclip allows easy and reliable management of attachments. Using it is as simple as adding a few columns to your model and invoking the has_attached_file class method with the desired options.

One of Paperclip's as-yet-undocumented features is the option to pass an anonymous function to the :url, :path, and :styles parameters of the has_attached_file class method.

In the example below, a Product has many ProductImages. No problem, except that I might not want every product image to have the same style attributes (e.g., a product could have a header image of '200x400' and a main image of '300x300'). Certainly one way to handle this would be to create a distinct class for each type of product image (HeaderProductImage, MainProductImage, etc.).

But another, and perhaps more interesting solution, is to pass an anonymous function to the :styles parameter. That function can then return a customized style attribute for any kind of image:




class Product < ActiveRecord::Base

has_many :images, :class_name => "ProductImage"

end

class ProductImage < ActiveRecord::Base

belongs_to :product

has_attached_file :image,
:storage => :filesystem,
:path => ":rails_root/public/images/products/:attachment/:id/:style/:basename.:extension",
:url => "/images/products/:attachment/:id/:style/:basename.:extension",

# Because the :styles key can take a Proc object as an argument, we can specify the image
# styles dynamically based on the image's label.
:styles => Proc.new {|this|
defionition = ProductImageDefinitions.detect {|spec| spec[:label] == this.instance.label}
definition[:styles].merge(:thumb => '200x200>')
}

end


When Paperclip processes the attachment, it passes itself to the anonymous function. The model can then be accessed by calling the #instance attribute on the passed-in reference (see above).

The ProductImage class defines a #label attribute. The label acts as a key to a hash, ProductImageDefinitions, which specifies styles for various kinds of product images (you might define this somewhere in config/initializers).




ProductImagesDefinitions = {
'main' => {:styles => {:large => "1200x600!", :medium => "600x300!"},

'header' => {:styles => {:large => "400x600!", :medium => "200x300!"}
}


This allows for a high level of flexibility since adding a new kind of product image is simply a matter of modifying the ProductImageDefinitions hash. For a nice interface, you might write a module like this one to allow for the following manipulation of product images:




# Create product images.
@product.product_images.create(:label => 'main', :image => # Attachment object)
@product.product_images.create(:label => 'header', :image => # Attachment object)

# Access product images.
@product.available_images('main')
@product.available_images('header')


Not bad, right? The same kind of on-the-fly configuration can be applied to the :url and :path parameters. Thus does Paperclip's support for anonymous functions enhance an already superb library.

For the record, I myself am still on the fence about whether this application of anonymous functions can be considered a good practice in terms of readability and maintainability.

But from the perspective of the sheer fun and practicality of the functional paradigm in Ruby, I'm going to consider it a worthwhile excursion.



Labels: , , ,

Introducing @codeoff - coder competition and support in Twitter

Have you ever been in the middle of a project, needed a few quick lines of code, and Google wasn't quite helping you out?

Are you the kind of person who enjoys taking a few minutes from time to time to help out your friends with your coding prowess?

We created a new social coding utility called @codeoff just for you. Use the Twitter @codeoff account to reach out for quick programming help and/or exercise the good Samaritan in you by responding to quick requests.

We're also hoping @codeoff awakens the competitor within the coder, as people fight to be the fastest/best in posting requests and sharing code replies.

Need some code?
1. Follow @codeoff on Twitter.
2. Tweet code you need to @codeoff:

Format:
@codeoff language requirement
Example:
@codeoff python: writer a retweeter

3. Wait for @replies :)

Code some code?
1. Follow @codeoff on twitter.
2. Look for code tweets.
3. Code.
4. Post your code to http://gist.github.com.
5. @reply to @codeoff, including the requester and your gist url.

Format:
@codeoff @user http://gist.github.com/gistid
Example:
@codeoff @apumapho http://gist.github.com/101263

We hope you enjoy using @codeoff.  Drop some comments and let us know your feedback.



Labels: , , ,

Tip: Conditional Validations for ActiveRecord

Is isn't always desirable, or even necessary, to validate every attribute in a given model with every save. For example, I recently had to build out a tabbed interface for editing products. Normally, when a user would press save, with the controller code calling update_attributes, errors would appear for attributes outside the current tab.

The need for intelligent, conditional validation begat the following module, which can be included in any ActiveRecord class:



module SelectiveAttributeValidatable

def self.included(base)
base.send(:include, InstanceMethods)

base.class_eval do
alias_method_chain :update_attributes, :check
end
end

module InstanceMethods

# Store the set of attributes passed in to #update_attributes.
def update_attributes_with_check(attrs)
@attrs_to_validate = attrs.keys.map(&:to_s)
update_attributes_without_check(attrs)
end


# Returns true if a given attribute was passed in to #update_attributes.
def should_validate?(attr)
if self.new_record?
attrs_to_validate_on_create.include?(attr)
else
@attrs_to_validate.blank? ||
@attrs_to_validate.include?(attr) ||
self.status == 'ENABLED'
end
end

# This method must be overwritten in the class
# where this module is included. Specify whichever
# attributes you want validated on create:
# ['name', 'sku']
def attrs_to_validate_on_create
[]
end

# Allows the should_validate callback to be thrown.
def method_missing(method, *args)
if method.to_s =~ /^should_validate_([_\w]+)[?]/
return should_validate?($1)
else
super
end
end

end

end



After including this module, you'll need to add conditions to your validations, as follows:



validates_presence_if :description, :if => :should_validate_description?
validates_numericality_of :age, :if => :should_validate_age?


Once you've implemented the conditions on your validation, any call to update_attributes will validate only the attributes included in that call.

Caveat

You probably want to make sure that your objects are eventually valid. In my case, this entails storing an enabled/disabled state on the object. This way, I can specify that when the object is enabled, all of the validations should be in effect.

Ruby Did My Taxes

Over the weekend, like many Americans, I did my taxes. I'm required to file a couple of different Schedule C's for various side-businesses I have, and when it comes time to hunt for deductions, it sure is nice to be a programmer.

I had initially thought that I would be able to download the entire year's bank transactions into a CSV file, but alas, my bank (and many others, I'm finding) only provide 45 days of history. They do, however, allow you to download PDF files of all of your statements, and so there I was staring at thousands of transactions in PDFs, dreading retyping them into Excel.

Enter my skills as a Rubyist. I sought out the PDF::Reader library, which allows you to hook its parsing engine to a custom callback and do what you want with it. This definitely parsed the PDF fine, but I had no context; no idea where I was in my statement, because there's no callback for a "new line" character - it's just a stream of words. I found that if I used Adobe Acrobat to save the files as text-accessible, then I started to have statements I was able to work with.

Now that I had 'lines', I was able to use the power of regular expressions to get the data I needed. The lines I was interested in started with a date and an amount, and the rest was just description for my transaction.

Here's the warts-and-all code I used to compile the year's worth of spreadsheet data. It's truly "quick-and-dirty", but it saved me tons of time. The next step, of course, would be to implement regular expressions against the 'memo' field of these transactions, and pre-suppose categories and deductions based on these patterns. But then again, maybe it's time to just use Quicken and stop waiting until the last minute.



Labels: , ,

Reuse Code And Write Reusable Code

reuse-code.jpgBefore I set out to write a significant amount of code, I search high and low to make sure someone else hasn't already written it.

There are a few ways to do this:
1. Google Code Search
2. Look in your other projects for similar code.
3. Ask around (co-workers, twitter, IRC etc..).

One main reason for code reuse is that it will usually have less bugs than writing it from scratch. It is also evident that reusing code will save time. For example, why write a user authentication system when you can just download one and install it as a plugin. A few hours of research can save hundreds of hours programming. Another advantage to using pre-existing code is that it is usually more abstract and can fit a wider variety of applications. As a bonus, it usually has more features.

When you do have to write code because the problem you are solving requires more customization than anything out of the box, it is best to make sure it is reusable. For example, avoid naming your variables too specific, such as the_yellow_square_at_the_left_of_the_page, instead, create a function similar to shape('yellow', 'square', 'left'). This will allow you to reuse it for many situations. If you wanted a purple triangle on the right you would just write shape('purple', 'triangle', 'right'). It's much easier to refactor and reuse on future projects.



Labels: , , ,

Django-svn-revision: A Django pluggable App To Display Revisions In Views

2009-03-11_1116.pngDjango-svn-revision is a Django pluggable app that can be added easily to your existing Django application.   It gives you the ability to embed the revision into the meta tags or any other place in your views.  

This originally started out as just a template tag that would call svnversion every time the page loaded.  Then we refined it to use an __init__.py file beforehand to limit resources.  Finally we converted it into a pluggable app so it will work easily with other applications.  

One useful application is to help out the QA process by enabling testers to match up revisions.  Django-svn-revision is available on our github repository for download. Feel free to use it in your project.  Let us know how it works out!



Labels:

Tip: Reloading ActiveRecord Instances

When writing unit tests in Rails, I'll often make a change to an ActiveRecord instance, requiring a reload for the next assertion to pass (oversimplified):


context "A new group" do 
  setup do
    @group = Factory(:group)
  end

  context "with an added entry" do
    setup do 
      @entry = Factory(:entry, :title => "WEB", :group => @group)
      @group.reload
    end

    should "add an entry to the group"
      assert @group.entries.include?(@entry)
    end
  end
end

Instead of calling #reload on all the affected objects, I've found it useful to include a simple helper method that, when called, reloads any in-scope ActiveRecord instances:


class Test::Unit::TestCase
  private
 
  def reload_activerecord_instances
    self.instance_variables.each do |ivar|
      if ivar.is_a?(ActiveRecord::Base) && ivar.respond_to?(:reload)
        ivar.reload
      end
    end
  end
 
end


This has proven especially helpful when I'm handling several ActiveRecord objects and need to update them all at once. Cleaner, less repetitive code.



Labels: , ,

Dissecting the Green Key Marquee

The recent redesign of professional recruiter and temporary staffing firm features an interactive marquee that combines a slideshow, areas of expertise menu kick-out, and zoomable touts highlighting the site's three key areas: available positions, resume submission and employers portal. This is a dissection of the component's front-end HTML and CSS architecture, giving a more in-depth look at what's actually happening under the hood.

The Marquee Frame

The marquee's frame consists of the following divs:

  • #marquee - main parent container
  • #marquee_head - transparent png set as the background for the top curve
  • #marquee_foot - transparent png set as the background for the bottom curve
  • #marquee_content - content with a repeating vertical shadow background image
  • #marquee_overflow - wrapping container with overflow set to hidden to clip excess content from appears outisde the curves and sides

Each of the background images have a white outset to allow for the clipping to display correctly.

Zoomable Touts

gk_zoom.jpg

Keepin' it cross-browser

The challenge here was keeping the tout's markup within the actual marquee parent container to maintain a nice markup flow. Since IE disallows childen to stack over its parent elements who have an overflow set to hidden, this presented a problem for the hover states on each tout. As seen in the image, the touts overlay the actual marquee frame when hovered over with a cursor.

The solution?

Gotta swallow the geek pride on this one. We have to break the markup's natural flow a bit and place the hover states, #marquee_touts_active, outside the #marquee div. Fortunately the placement is immediately after the #marquee div so the markup's "roadmap" is still intact.

Transparency

No png's necessary. The tout's transparency setting is fully CSS-controlled. To cover all bases a few separate browser-specific CSS statements are necessary (see below).

Areas of Expertise Menu

gk_menu.2jpg

This menu is an overlay kick-out that appears when you hover over its call to action underneath the marquee's main blurb that lists areas of expertise which are specific to the current division you are viewing.

See for Yourself

Below are stripped down versions of the actual HTML and CSS. You can view the marquee live in action at any of Green Key's divisional sites such as Accounting & Finance.

HTML

			
<!-- #marquee-->
<div id="marquee">
	<!-- #marquee_head -->
	<div id="marquee_head">Beginning of Marquee</div>
	<!-- /#marquee_head -->
	
	<!-- #marquee_content -->
	<div id="marquee_content">
		<!-- #marquee_overflow -->
		<div id="marquee_overflow">
		
			<!--.slide -->
			<div class="slide">
				<!-- slide content -->
			</div>
			<!-- /.slide -->
							
			<!-- #marquee_touts -->
			<div class="clear" id="marquee_touts">
				<div class="tout">
					<!-- tout content -->
				</div>
			</div>
			<!-- /#marquee_touts -->

			<!-- #menu_areas -->
			<div id="menu_areas">
				<!-- areas of expertise content -->
			</div>
			<!-- /#menu_areas -->
		</div>
		<!-- /#marquee_overflow -->
	</div>
	<!-- /#marquee_content -->
	
	<!-- #marquee_foot -->
	<div id="marquee_foot">End of Marquee</div>
	<!-- /#marquee_foot -->
</div>
<!-- /#marquee -->

<!-- #marquee_touts_active-->
<div class="clear" id="marquee_touts_active">
	<div class="tout"><!-- tout content --></div>
</div>
<!-- /#marquee_touts_active -->
			
			

CSS:

			
/* #marquee
---------------------------------------------------------------*/
#marquee {
	margin:0 0 0 -10px;
	width:961px;
}
	#marquee #marquee_head {
		background: url(/img/bg/bg_marquee_head.png) 
				no-repeat scroll 0 0;
		_filter:progid:DXImageTransform.Microsoft.
			AlphaImageLoader(
				src='/img/bg/bg_marquee_head.png', 
				sizingMethod='image'
			);
		_background: none;
		height:43px;
		position:relative;
		text-indent:-1000em;
		width:961px;
		z-index:2;
	}
	#marquee #marquee_foot {
		background: url(/img/bg/bg_marquee_foot.png) no-repeat;
		_filter:progid:DXImageTransform.Microsoft.
			AlphaImageLoader(
				src='/img/bg/bg_marquee_foot.png', 
				sizingMethod='image'
			);
		_background: none;
		height: 60px;
		width: 961px;
		position: relative;
		z-index: 5;
		text-indent: -1000em;
	}	
	#marquee #marquee_content {
		background: url(/img/bg/bg_marquee_content.png) 
				repeat-y scroll 0 0;
		height:391px;
		position:relative;
	}
		#marquee #marquee_content #marquee_overflow {
			height:446px;
			margin:-25px 0 0 10px;
			overflow:hidden;
			position:absolute;
			width:940px;
		}
		
/* #marquee_touts
---------------------------------------------------------------*/
#marquee_touts {
	position: absolute;
	top: 450px;
	z-index: 1;
	background: #236336;
	filter:alpha(opacity=85);
	-moz-opacity:0.85;
	-khtml-opacity: 0.85;
	opacity: 0.85;
}
	#marquee_touts .tout {
		float: left;
		width: 311px;
		padding: 23px 0 45px 0;
		border-right: 1px solid #77917e;
		cursor: default;
		height: 50px;
	}

/* #marquee_touts_active
---------------------------------------------------------------*/
#marquee_touts_active {
	position: absolute;
	z-index: 100;
	margin: -165px 0 0 0;
}
	#marquee_touts_active .tout {
		height: 153px;		
		position: absolute;
	}
		#marquee_touts_active .tout .tout_content { 
			height: 153px; 
		}
		/* extra div for ie hasLayout */
		#marquee_touts_active .tout .tout_content {
			padding: 30px 0 0 0;
			background-position: left top;
			background-repeat: no-repeat;
		}

/* #menu_areas
---------------------------------------------------------------*/
#menu_areas {
	position: absolute;
	z-index: 2;
	top: 235px;
	left: 34px;
	padding: 33px 0 0 0;
	width: 578px;
}
#menu_areas.active {
	background: url(/img/bg/bg_menu_areas_head.png) 
		no-repeat left top;
	_filter:progid:DXImageTransform.Microsoft.
		AlphaImageLoader(
				src='/img/bg/bg_menu_areas_head.png', 
				sizingMethod='crop'
		);
	_background: none;
}
#menu_areas.active .menu_content { display: block; }
		
			
			


Labels: , , ,

Mortimer: A Rails Password Manager

Today we release mortimer, our Rails-based password management application. The goal was to produce a secure, multi-user password vault providing basic user permissions and a simple interface. Here's a screenshot from the current app:

mortimer.png


Public-key Cryptography

mortimer secures password using public-key cryptography. Each user on the system has a unique key pair. When you create a password entry, mortimer stores a uniquely-encrypted version of that password entry for each user with access to that password. This ensures that any time a password changes, all users have access to the change, with no compromise in security. And since all private keys are symmetrically encrypted with the user's password, even a compromised database is fairly useless.


Precautions

Many would still argue that "web application" and "password manager" are mutually exclusive terms. It depends. We recommend, at minimum the following:
  • Do not expose mortimer to the public internet.
  • Run it over SSL (this is, in fact, required).
  • Use strong passwords, and limit access to the production environment.

A work in progress

mortimer should be considered alpha as it will remain under active development. Expect improvement to the UI, along with security tweaks and cross-browser compatibility. Let us know if you find it useful.


Contributions are welcome. Clone or fork us on GitHub.



Labels: , ,

Validify, Semantify, Accessify

Tom is Ai's front-end tech lead.

The following is a developmental check-list I religiously adhere to and find extremely essential in ensuring semantic-rich, accessible and valid (x)HTML.

Cool Dood Holding Checklist

Da Basics

  1. Specify a DOCTYPE and namespace
  2. Specify the document's language
  3. Write your markup before writing any CSS to ensure your HTML is semantic and well-formed
  4. Document your HTML with start and end comments for clear separation of layout blocks within your markup
  5. Make sure the page validates (x)HTML
    1. 1.0 Strict for all new development
    2. Transitional for legacy applications using HTML flavors from the past
  6. Make sure the page title is set
  7. Include all necessary meta tags
  8. Use block and inline elements appropriately
  9. X-browser/X-Platform the page
  10. Separate blocks of content with a div
  11. KEEP ALL ATTRIBUTES AND THEIR VALUES LOWERCASE...KICKIN' IT OLD SKOOL IS COOL, BUT NOT THIS WAY

Presentation and Content — Keep 'em Separate

  1. Make sure your markup is semantic in that proper elements are used in which their purpose was intended
    1. Layout => div
    2. Headings => h1 through h6
    3. Paragraphs => p
    4. List of items => ul, ol
    5. Content emphasis => em, strong
    6. Tabular/grid data => table
  2. The presentation of content should be consistently applied regardless of CSS

Name Your Children With Some Structure

  1. Name your ID's and classes based upon the structure of its content, not presentation
    1. content_head
    2. content_main
    3. sidebar
    4. navigation_main
    5. navigation_secondary
    6. logo
    7. header
    8. footer
  2. This will prevent renaming if the content's look and feel changes

Headings Schmedings

  1. One h1 per page
  2. Maintain the hierarchy, h1 -> h2 -> h3 ... h6 regardless of how cosmetically they may appear in design

Pretty Pictures

  1. Every image must contain a non-empty alt tag
  2. Dimensions on every image
  3. Keep images used for photographical purposes inline, not as background images

I Call 'em Links, Anchors are for Boats

  1. Include title attributes on important links and/or graphical links containing no text in the image itself that describes the destination of the link
  2. Make sure there is always text present within the anchor even if it is a graphic
  3. The text should be informative, ie - use 'close' rather than just 'x' for a close button

Fontography

  1. Try to avoid all caps within markup and allow CSS or emphasizing elements to properly transform text so you have the option of reverting if needed
  2. Don't abuse the br tag, using multiple consecutively isn't necessary, just begin a new block level element

Set the Table, Anthony, and Don't Forget the...

  1. caption element to let users know what your table is about (look at it as short headline)
  2. summary attribute to further describe your table's contents in more detail
  3. colgroup's for multi-column tables
  4. th's for column headings
  5. thead for table heading content, tfoot for table's footer content and tbody for the main body of the table's grid content
  6. tfoot comes before the tbody in the markup...don't ask me why
  7. And for pete's sake, don't use tables for layout, that's so 1999

Forms (Don't Have Anything Cute for this One, Sorry)

  1. Don't forget the action and method attributes (leave em blank if you don't know, leave that mush to the back-end dude)
  2. Have a legend to set the title of the form
  3. Use fieldset's to group related fields
  4. Use div's for further content separation within fieldset's
  5. tabindex on each input for easy keyboard accessibility
  6. The corresponding text for each input should be a label with the for attribute set to the input's ID
  7. maxchar set on inputs where necessary to assist with validation


Labels: ,

Dramatically Reduce Form Submission Spam With A Hidden Field

You've set up your site and its contact form and you have started getting traffic. Then one day the person getting the contact requests forwards you a spam submission:

"We are getting tons of these. Is there a fix our web guy can implement?"

Your immediate response may be to install a captcha. That will work, although it is a bit much for a simple contact form.

An alternative solution is to simply include a hidden field.
hidden-field.pngIn your css file add:
#email2 { display: none; }

In your contact form add:
<input class="text" type="text" name="email2" id="email2" />

Finally, in your server side processing script add logic to the effect of:
if request.POST.get('email2','')=='':
    #process form

And that is it. This will trick the spam bot to think that there is an extra email field in your form that needs to be filled out. It will automatically fill it out and submit the form. By filtering any form submissions that have this extra field entered, you can exclude the non-human submissions.



Labels: , , , ,

iMacros for Firefox: Automate time consuming repetitive processes

typing-robot-thumb.jpgWhen testing a web form, it can be tedious to constantly enter in the data over and over, especially for multi page forms. Auto form fillers are good but they don't automatically submit the page and fill out subsequent pages.

I have tried a few macro recorders and one has become my go-to for testing forms. iMacros for Firefox is a free Firefox plugin that allows you to record and play back macros.

What I like about it:

  1. Macros are simple text files that can be edited in a text editor.
  2. The macro language is very extensible and can perform most any web task.
  3. You can share macros through a link.
Another free macro utility (Windows only) I recently found is AutoIt. AutoIt has more coverage of things it can do since it is not dependent on a browser. The macro language is also very capable of many tasks.

A more advanced plugin that can achieve the same automation results is Selenium IDE. It is more versatile than the previously mentioned macro recorders, since it can be fine tuned to run during an automated test process directly on a server. Selenium IDE is also free and works wherever Firefox does.

Whichever you choose, the time saved by running macros can greatly improve your programming efficiency, allowing for more time for other tasks--like foosball.



Labels: , ,

Kick your footer into gear with a dynamic copyright

Year after year we end up changing copyright footers. Here are some snippets that will automatically update the year in your copyright text.

Django:
{% now "Y" %}

Ruby on Rails:
<%= Time.now.year %>

PHP:
<?php echo date('Y'); ?>

Smarty:
{$smarty.now|date_format:"%Y"}

JavaScript:
<script type="text/javascript">    
 document.write(new Date().getFullYear()); 
</script>


Labels: , , ,

Throttling ActiveResource

Rate limit. It's an essential, if somewhat bothersome, consideration in accessing any public API. Twitter's API limits its clients to 100 requests per 60 minutes. Exceed that, and you, the client, are locked out for a good ten minutes.

The other day, I was using ActiveResource to build a library to interface with the Harvest API, which implements a rate limit of 40 requests per 15 seconds. While a generous limit, I knew that the reporting tool I was building would need to make a lot of requests. And I was indeed getting locked out.

Since ActiveResource provides no throttling functionality, I decided to build a simple gem to add it. Thus was born ActiveResourceThrottle.

To use, create a generic base class to access the given RESTful API. Then, use the throttle method to specify the request limits you'd like imposed. Requests originating from any subclasses, representing the various API resources, will be throttled accordingly.

require 'active_resource_throttle'
class Twitter < ActiveResource::Base
throttle(:requests => 100, :interval => 3600, :sleep_interval => 10)
end

Further documentation can found in the README. Happy Throttling!



Labels: , ,

Ai at Apachecon

Ai technical director Martin Anderson and senior developer Ed Samour are speaking at Apachecon in New Orleans this week.

Developers attending the conference should visit the OFbiz symposium Thursday to see their presentation, How I Learned to Stop Worrying and Love OFbiz. The rehearsal was fascinating and fun--don't miss it.



Labels: , ,

Web 2.0 Expo: thoughts from David

Ai technical lead David Yoon made a great compilation of Web 2.0 Expo observations, below. This wraps our conference coverage.

General points from the conference:

Technology: the conference from a technology perspective was very heavily focused on just a few topics:
-- cloud computing and the problem of scaling websites
-- next generation of browsers and bringing web applications even closer to desktop applications
-- graphics and the re-emergence of Javascript as a hugely important language

Wednesday

Lessons from Visual Programming with JavaScript: John Resig (creator of jQuery) talked about Processing.js, a new library that he created to help interact with the canvas element.

10 things we've learned at 37 signals
--I thought this was one of the better talks of the conference
-- planning is overrated/decisions are temporary/optimize for now
-- create waves of interest -- momentum is very important
-- interruption is the enemy of productivity
-- out-teach, out-share, out-contribute
-- most of these points are applicable for small, non-client facing tech shops

Go REST with Rails
-- DHH gave a great talk about REST and Rails, aka reasons for a restful architecture (mainly for communication/interaction between sites), and discussed some of the features that are built into the framework to handle it. Most developers here already know the principles/reasons but it was a good presentation.
-- Support in rails 2.1 for etags/not modified headers

Thursday

Building in the Clouds: Scaling Web 2.0
-- Panel discussion about cloud computing from a variety of companies (google, 10gen, amazon), where it currently is and how to make best use of it.
-- Ability to scale massively in a very short amount of time - example of the facebook photo application that scaled from 5 servers to 5000 in a week using amazon.
-- Not suited to all application/companies, developing for cloud applications requires a shift in developer mentality.
-- Relevancy/importance of cloud computing

Designing for the Internet(s) of the future
Very interesting talk by Genevieve Bell - anthropologist researching trends in global internet usage
-- China now has more active internet users than the US, this trend will continue
-- Internet is becoming widespread in the developing world, though generally it's become shared (1:many people per device), asynchronous (delay of days/weeks between the transfer of information)
-- Trend toward disconnecting from the internet
-- Concerns: Privacy issues, cultural health

The sequel to SQL: Why you won't find your RDBMS in the clouds
Great technical talk about object databases/cloud computing.
-- scaling databases: partitioning (sharding) over multiple servers is hard/slow when using relational databases
-- object databases
- examples: BigTable (google), SimpleDB(amazon), Mongo (10gen)
-- pros: scaleable
-- cons: - no joins across tables...
- eventually consistent (not good for banking transactions)
- query limits (1000 entities for google, 250 for amazon)

Friday

State of Web Development
-- chrome/gears, web applications are becoming more and more like desktop applications
-- js will become *really* fast in the next generation of web browsers, the language will potentially have much more influence
-- rise of Ajax as the important medium for providing a better user experience.



Labels: ,

Web 2.0 Expo: thoughts from Sean

Guestblogging today are David Yoon and Sean Auriti, two of Ai's developer team, who attended the conference and shared their observations.

Here are Sean's thoughts. We'll post Yoon's on Monday.

Sean

"REST with Rails" was a good refresher with some good points on keeping the code DRY as well as providing different formats of content using a single url.

Great takeaway: keep it restful, every resource should have its own controller.

I learned some things on the new rails 2.1 caching methods... can change etags, expire time. Saw how simple coding can be by using rest methodology. Very easy to implement, atom feeds, api calls and csv exports.

Presenter David Heinemeier Hansson is the creator of Ruby on Rails and a partner at 37signals.



Labels: , ,

Web 2.0 Expo: Me and We, and Seduction

Barry Libert's "We and Me" presentation at Web 2.0 was certainly enthusiastic. In reviewing my notes, I'm noticing that if you combine his concepts with Chris Fahey's session on seduction, you've really got something....

Businesses are not good listeners. The good news is that Web 2.0 allows them to create conversational websites that lure customers in (Barry). To do this, the sites need to dazzle, amuse, and deliver with flair (Chris). Forget about "what I can tell you" (Barry). Flatter them, tempt them, create mystery (Chris).

Be rewarding--use contests and givebacks combined with open communication (Barry). Plan for delight, and evaluate the results with psychology and emotion (Chris). Seduce (Chris). Converse (Barry). And see your sales and satisfaction levels rise.



Labels: , ,

Web 2.0 Expo: service as marketing

One of my favorite sessions at last week's Web 2.0 Expo (and I'm not just saying this because he's an old industry friend) was Lane Becker's Customer Service Is the New Marketing. Lane runs Get Satisfaction and sees these things firsthand. His insights were smart and useful for anyone selling products in the 21st century.

Lane's big takeaway: "Act like a hotel concierge." Stark and obvious, it is nevertheless an important reminder to anyone in business. Consumers treated with respect and a can-do mentality will develop loyalty and appreciation above and beyond a basic liking of product or service.

This theory is important for us locally, both inside and outside Ai. We don't have an account management team, which makes our project managers (and our president, and assorted other employees) directly responsible for keeping our clients happy. This makes communication a priority and minimizes siloed output, both of which Lane cites as vital to success.

Lane's suggestions have a more obvious application with our clients, many of whom run successful ecommerce businesses. The more they listen to customers and gear their sites toward client satisfaction, the better they will be at pleasing and retaining business. And as Lane noted, even a modest increase in customer retention can nearly double a company's profit.

These concepts also touch upon a recurrent theme of the expo (and of Web 2.0 in general): the power of users to beneficially transform businesses. The time has come for companies to embrace the shift in customer communication.



Labels: , ,

Putting Your Web Site in the Cloud

How many CPUs do you have?

One of the most interesting changes to the web in the last couple of years is that computing hardware is moving beyond mere commodotization and into the realm of the metered service. Companies that formerly would have had to heavily invest in hardware (and the associated system administration costs) in order to deploy web sites are beginning to have a viable alternative: cloud computing, available in various flavors such as Amazon's EC2, Slicehost's virtual machine based hosting, or quick set-up hosts like WebFaction.

Instead of buying hardware to host your website, it is now possible to rent computer time at any capacity level. This allows businesses to essentially pay for only the computer power they need.

This is great, but implicit in this is that we need to change the way we build web applications. What is rapidly becoming the predominant hardware paradigm (via cloud computing) for web applications is in a direct disconnect with the technologies that we're using to build them.

It has to do with how many CPUs (processors) the technology can handle.
The approach-du-jour in web apps is rapid development through frameworks that take advantage of powerful, dynamically typed languages like Ruby and Python. These frameworks allow people to build web applications very, very fast.

However, these languages are executed in runtimes that are not SMP (symmetrical multi-processing) enabled. In other words, the runtimes can only handle one processor at a time. There are numerous hacks in the web development world to get around this, mostly involving running multiple instances of the runtimes. However problems creep up - the well known scalability problems that Twitter has encountered have their roots in these issues.

So up on the bleeding, ragged edge of web development, we're starting to look at alternatives from other industries. In the telephony world they've had to deal with these kind of scalability and reliability issues long before we were around, and they have some pretty impressive solutions.

Erlang, developed by Ericsson, is a language and runtime created from the ground up to live in an SMP world (originally its "cloud" was telephony equipment). There is a famous Erlang app which claims nine nines of reliability. That's 99.9999999% uptime, or a few seconds every 30 years. Way more than any website needs.

Unfortunately, Erlang is, well, kind of weird. People coming from said Python or Ruby background will have to re-learn some very fundamental approaches to problems, and that's not going to be easy. In fact it might not even be possible. Fortunately, Erlang compiles to bytecode (similar to the way Java and Python work) meaning that its possible to write a different compiler and perhaps uses a different language on the Erlang runtime (called BEAM).

So just today I noticed, (in what is clearly a really, really early incarnation) - a little project called Reia. Its pretty raw now, but it purports to be a Python/Ruby-like high level language that compiles to Erlang BEAM format, giving it all the SMP-loving, fault-tolerant and distributed goodness that comes with Erlang. I don't know if Reia is the winner, but I feel pretty confident that creating a friendler programmer interface to the Erlang runtime is a winning proposition.

Wither Windows?

So quite recently, the Ruby on Rails open source web framework announced that they would be migrating from the Subversion code repository they had to a new one managed by Git.  Git is a version control system created by Linus Torvalds to manage the Linux kernel.  Linus had several requirements in mind when he made Git, requirements that involved specific sets of features, scaleability, stability etc.


As might be expected from the creator of the Linux kernel, none of these requirements included running well on Windows.

Git does technically run on Windows, but its kind of a hack, and Redmond's favorite platform is definitely treated like a second class citizen (ooh... irony...).  So naturally when Rails moved to Git, there was a number of Windows users who were concerned they were being left behind.  Interestingly, the Rails maintainers responded that amongst the core developers of Ruby on Rails, Windows users were a small minority.

So then, in this other piece I was reading (I need to see at least two things before I declare an Official Trend) John Dvorak rips on Dell, claiming they're stuck in a 90's mentality.  In the article, he says Dell isn't keeping up and startups in Silicon Valley these days tend to use laptops, and many many of these laptops are Macs.

Even Senator Schmelkin, a long time Windows guy, switched completely over to a Mac a couple of months ago (I tried to get him to blog it...sorry, no luck...).

Okay - I knew Apple was getting a boost from the whole  iPod thing, but I never expected to see quite this level of momentum (and yes, yes...I'm sure in the accounting and parking facility businesses Windows still has 18456% market share...).  There seems to be an accelerating trend, especially in the software and web world where not only is it more desirable to work on a Mac, but its beginning to look like people are beginning to take the position that Windows doesn't matter.  It's like it's deprecated.

(Disclosure - I was a Mac guy from before it was cool, except for a span of about 5 years that I spent trying to install Linux on a laptop).

The Rails guys do tend to be a bit religious at times - "my way or the highway".  But I do find the basis for their switch interesting.  The lack of first-class support for Windows was simply not a consideration.  Has the world finally changed?  Is the wicked witch finally dead?



Labels: , , , ,

Layers

Seth Godin: "We live in a layered world now. Those that plan and plan and then launch are always going to be at a disadvantage to the layerers."

So true, and "now" doesn't just mean "of late." Web professionals have long promoted the fluidity of their work. It's the beauty of the thing: one can always redo, change, expand, improve. Some lament the constant beta status of startups, but the evolutionary ability allows them to succeed.

Within this context, of course, one has to look out for the "good enough" trap. Is the project really good enough to launch? If so, put it out to the world. If not, don't rush into it for the sake of existing. Make it good, not good enough. Then stick it out there, even if it's missing the 38 features that are supposed to be in the final release phase.

Layering and iterating allows the potential audience to help define it. This is not just important; it can be career-altering. Flickr was a game until the image-posting app became too powerful to ignore. Hummers were military vehicles until Arnold Schwarzenegger asked AM General to make him a civilian model. Layering on multiple models, features, and ideas led these companies to successes far more vast and different than originally proposed.

So get that launch out the door and see what happens. Add layers as time allows and as market needs demand. And be willing to change course. It's not just the layers--it's the minds behind it that help dictate the success.



Labels:

Search Design

How do you find the information you're looking for on the Internet? This, of course is the original reason the Web was created - as a publishing platform for the physicists at CERN.

As the web matured, people constructed websites with tree-like structures, viewable as a site map. As web sites grew in size and complexity, we started referring to a large amount of information organized in a tree structure as a taxonomy. Some people (I'm looking at you, Yahoo) tried to taxonomize the entire web. Here's what we found out about taxonomies: they don't scale. Once they get too big, they start to collapse under their own weight. Historically it was a mark of a professional that they could navigate a large taxonomy, such as the Dewey Decimal System, or Scientific Classification. I don't believe that we'll see many new taxonomies of that scale adopted by the world at large.

So that leads us to search. Search, of course, is the other method we have to find the information we're looking for, especially when all we want to do is get to the information, and we don't give a rat's hiney how it's catalogued. But since the average search length on Google is something like 1.3 words, we need to infer a tremendous amount of information from very little input.

To me, this seems like a tremendously deep opportunity for the developers of websites to shape how information is presented via the establishment of business rules modeled within search. I call this Search Design, and I see it as a critical part of how websites with large volumes of information should be built.

Search design is currently implemented primarily in terms of how information on a website is indexed. This indexing controls the relative importance of information, and the relative importance of different structures inside the information, and is generally implemented by the developers who built the website.

However, at a higher level, search design is something that can be expressed as a series of business rules. Say you have a newspaper site with articles and authors. You could establish the following business rules for search:

  • Articles have higher priority than Authors
  • Weight of an article should be, in order of priority: Article Title, Article Author, Article Tags, Article Body.
  • Weight of an author should be: Author Name, Author Department, Author Tags, Author Bio

It could be a lot more sophisticated than this of course, it could contain relative numeric values for the indexed values, and the business rules surrounding relative importance of information could also be far more sophisticated.

Search scales. It can handle a LOT of information, but as implemented on individual websites, the presentation of it to date has been relatively naive. Search design could grow to be a profession in itself, much in the same way that information architecture developed over the last decade. For many sites it is search, not a taxonomy structured navigation, that is the primary means of navigation for a website.



Labels:

Your Agile Process Sucks

I am so sick of people throwing the term "agile" around like some sort of magic pixie dust that can be sprinkled on projects, making them somehow robust, easy to build and otherwise wonderful. Like any popular idea, it has been hijacked by the same people who screwed up permission marketing, business process reengineering, and other things that started out as good ideas.

It's just a hammer - it's only good for nails. Not everything is a nail. There are two really irritating things going on:


  1. Ideas have boundary conditions. They're not randomly good for everything, they always have sets of conditions under which they work, and sets of conditions under which they don't work. Agile is not appropriately applied to every case.


  2. People throw around ideas they don't understand. "Agile" generally refers to a group of software development methodologies, including Extreme Programming, Scrum and Crystal. These processes do things differently from each other, and just saying "Let's do this agile" doesn't mean a whole lot by itself.

Just Because It's Best Practice Doesn't Make It Agile Let's build stuff in iterations! Yeah, that's agile! Uh, actually the concept of iterations somewhat precedes the whole agile thing - they just adopted it because, well, it's a good idea. Also items like code reviews, version control and a lot of communication with the client are not uniquely agile, they're just ways that we've learned are a good way to build software.
Calling it agile doesn't make it so To truly build something in a lightweight manner takes commitment and courage. Things like having small, quick releases, a flexible scope, little or no documentation. Far too many people throw the "a-word" around their developer process and then settle back into their 2000 pages of documentation.
Agile is not a business plan The grass is always greener on the other side for disciplines. Software developers (for some reason) seem to wish they were architects, so they steal concepts like "design patterns" and "information architecture" - trying to capture the architect mojo. Business, in turn, steals from tech - speaking of management concepts in terms of "bandwidth" and, yes "agile". To say "we're an agile business" is meaningless, and borders on intentional obfuscation - its an attempt to appear hip without actually making a commitment to any specific practice. Much like using the word "impact" as a verb, the easy mis-use of "agile" by people in the practice of nothing particular moves the term itself, which was sort of useful at one point, to a state of complete meaninglessness. I have to suppress the urge to snicker every time I hear someone flinging the magic pixie dust around. Maybe I'm allergic to it.

Labels: , ,

SAS, Outsourcing and Business Risk

Update:  This post has nothing to do with these guys.
I have long been a fan of outsourcing (not necessarily offshoring, which is a different animal).  Take the stuff that's not the core of your business and move it off to those who are experts in it.  I think it gets even better when that service can come through a nifty lightweight web 2.0 interface.  In other word, Software As a Service (SAS) UPDATE: Sorry - this should actually have been abbreviated "SaaS". Acronym fixed in the rest of this post.

So, along those lines, we here at Ai use a number of web-based outsourcing services to handle our non-core activities.  We use BaseCamp as a collaborative project management tool, and Harvest for time tracking, to name a few.  These decisions are cost-effective for us, because we're not in the collaborative project management tool business, nor the time tracking tool business.  We build websites for clients.
There's a catch, however (you knew the catch was coming, didn't you?).   There is a question of risk for each business function that is outsourced to a SaaS.  This risk can be difficult to quantify, so it can be a bit much for the more risk-averse business people.
SaaS-related business risk generally falls into two categories:
  • Availability:  How often does the SaaS go down, and become unavailable to its clients?  Depending on the level that business depends on the SAS, lack of availability can have a serious business cost, usually taking the form of paralyzing the business in some way.
  • Data Loss:  Much worse than availability, when mission critical data is put into an SaaS, data loss can be deadly.  Data loss can kill a business.  Dead.  A less deathly, but still damaging variant on this is vendor lock-in, where there's no expedient way  to leave the SaaS, because all of the business' data  is in the SaaS system, with no way to export it. 
Am I being hyper-cautious?  Are these risks just hypothetical?  Well, in the last week BaseCamp (actually all of the 37 Signals apps) went down because of a bad router.  There was no backup router in the rack, apparently, meaning that the entire suite of SaaS apps from 37 Signals had a single point of failure which, well, failed.  For all of Friday morning, no one derived any benefit from BaseCamp.
Last week Twitter (admittedly not the first thing one thinks of for business-facing SaaS, but still...) died unceremoniously during the Steve Jobs Macworld keynote.  Too many Apple fans live-blogging for Twitter to stand, apparently.
These risks slow SaaS adoption.  In order to make the leap across the chasm from early adopter types to the majority, SaaS providers must find a way to mitigate these risks.  This kind of thing  will be tolerated by a certain breed of early adaptors, but the more  conservative majority need more assurances.
Hello, SaaS providers - I'm talking to you now:
  1. Service Level Agreements for SaaS:  How about an uptime guarantee.  That is typically followed by an investment by the provider in the technical infrastructure to provide the agreed upon service level.  Like backup routers.
  2. Data backup and export:  Some assurance that a) our data is safe and b) we can get it away from you would go a long way to mitigating fears of certain business death in the event of a technical failure of an SaaS, or should we decide that we want to switch to another SaaS.
Now I'm not asking for regulation or anyone to pass a law or anything.  But it would be nice to have a certifying body out there somewhere for this kind of thing - a "Hacker Safe" for mitigating SaaS business risk.  That would provide businesses the assurance they need, and allow SaaS to fully cross over into the mainstream.


Labels: ,

Platform: Proprietary vs Open


Some time in the last 12 hours or so this went live.

OpenSocial is a common API for writing social networking apps. Its being embraced by practically every single social networking site out there, with the notable exception of FaceBook. Basically the promise of this is that an application developer can write an application against one API and then deploy it in any of the compliant social networks.

FaceBook, as the current king, has the least incentive to adopt the open standard. In fact the OpenSocial API can be seen as a check to FaceBook. Its simple to understand really, in place of "Facebook" use the word "Microsoft Windows" and in place of "OpenSocial", use "Java". The FaceBook strategy is to add value to their platform by harnessing third party developers to write to their proprietary API. OpenSocial is a counter-strategy to that, by making a universal API that is supported by all social networking sites.

The potential fallout from a successful deployment of OpenSocial is the movement of value in the supply chain away from the social network, and towards the applications. When an app can be deployed on any social network, it doesn't matter so much which network someone is on.

The natural response to this would be for various social networks to "embrace and extend" the OpenSocial API, to try and create lock-in for their own platforms. In the past Java had several legal protections in place to prevent this (not that Microsoft didn't try anyway). JavaScript, with none of the legal protections of Java, was forked mercilously, creating a difficult programming environment for JS developers that persists to this day.

Historically, Java failed at driving a wedge between Windows and application developers. It remains to be seen whether the social networking application community will standardize on FaceBook, OpenSocial, or something else.



Labels: , ,

The New Application Developers


The dirty secret of social networking website development so far - how do you actually get people on the site? In the past the path has been treacherous at best, consisting of a fair amount of traditional-style promotion and PR.

Now, major social networking sites such as FaceBook are presenting APIs. More than just surface integration, these APIs allow you to treat the social networking sites as platforms, meaning you can become a third-party application developer. Suddenly a lot of opportunity appears.

As is detailed here, anyone can write an application for Facebook that pulls information from a user's profile, including preferences, his network, friends etc. Users are, by default, encouraged to spread applications virally, and the better applications take advantage of the user's network.

Suddenly there is a viable alternative strategy to bootstrapping your own social network. You can simply build applications for Facebook, or other established social networks, and use their critical mass to hook into whatever you offer as a unique value. Of course the more of a purple cow you are, the more traction you'll gain.

Its amazing how full-circle this is. Suddenly web developers become like desktop application developers in the 80s and 90s, looking at their apps and deciding which platforms they want to target. Should we make a version for Facebook, LinkedIn and MySpace? Well, which is our target market? Hm....



Labels:

Wax in the Wild: PackRat



This is a classic WAX app - its a front end for 37 Signals BackPack, it has an offline mode, and to enable that it has sync. You can find it here.

It points down to the basic elements of WAX.

  • A rich client front end.
  • A web service with an API.
  • State synchronization between the client and the server.
PackRat is a great example of that.



Labels:

WAX in the Wild: Jing

A little while ago I talked about WAX - a new kind of application that combined the best parts of desktop and web applications. Well, via MacBreak Weekly I discovered a great little WAX app called Jing, made by TechSmith.
In a nutshell, Jing lets you take screenshots and desktop video and then easily and automatically share it on ScreenCast. The whole thing is currently designated a pilot project, meaning that its totally free.
There are really a number of options open to them for a business model, if they decide to go forward with Jing as a product. They could charge for the software. Images and video posted to ScreenCast could be associated with an advertising payload, making it a Google-type media initiative.



Labels:

Freezedown



The funny thing about creating a web site, web app or software project is that initially


  • Everything is up for discussion, then...

  • Nothing is up for discussion



This is because Project Variability is a time sensitive beast. And the best projects are ones where everyone, from the client, through the information architect to the developers and designers understand that the degree to which features are carved in stone is entirely dependent on when they are in the project lifespan.

The crux of this idea is that the tolerance for change in a project isn't an absolute - its a curve. Project's can undergo change midstream, but the tolerance a project has for change is in direct proportion to its remaining time. I call this phenomenon Freezedown: the degree to which features and implementation details are frozen in the project.

To try to express this lets compose a formula:


TV = F / T


where:


TV = Tolerable Variability: Change occurring against a feature set or implementation method.

F = Future Iterations Remaining in Project

T = Total Iterations in Project


So - lets say we had a project that was 16 weeks, broken up into 8 two-week iterations. Before the project development kicked off, there would be 8 out of 8 iterations remaining. That's "1" or 100%. At this point everything is on the table, there's no downside of changing the feature set, or the (at this point, planned) details of implementation.

As the project goes on, it can still accommodate changes, but the tolerance drops off:


  • Iteration 1: 87.5%

  • Iteration 2: 75%

  • Iteration 3: 62.5%

  • Iteration 4: 50%

  • Iteration 5: 37.5%

  • Iteration 6: 25%

  • Iteration 7: 12.5%

  • Iteration 8: Feature freeze



What do these percentages mean? They abstractly represent the amount of feature changes or implementation changes that are open for discussion. This is the tolerance of the project for change.

If something comes along that exceeds that level of change, at that time, the scope has changed - which at the very least means the original schedule is probably not going to be realistic any more. Everyone in the project should be aware of the degree of Freezedown in effect at any given point.



Labels:

WAX

Over the last year or so I've started to take notice of a new breed of online applications. I think that web-apps, exposed API's, mash-ups, widgets and so forth are a number of different views of the same overall trend. Namely:


  1. An online service that takes some silo of functionality and makes it available through many different means. This usually includes a web site, but can often involve many machine-readable formats such as XML-RPC, JSON, REST and so forth.

  2. A bunch of different client apps that connect it together: other websites, widgets / gadgets, full blown desktop apps or plug-ins.



Previously we would have said the business hosting the online service was a web site, or a web application. But what if the web was no longer the primary way in which it was accessed? For example, what exactly is the iTunes music store? If most people just accessed Gmail with a POP or IMAP interface, what would it be? What if the primary interface to Google Calendar was iCal, and not the web?

I'm going to put a stake in the ground and try to put a name to this thing: WAX. Web Application eXchange.

WAX consists of three core elements:

Web - not because it even has to have an HTTP interface, but because it usually does, and because this phenomenon is growing out of the Web community. HTTP has become the de-facto standard transport for applications, and almost all WAX apps use HTTP as their transport mechanism.

Application - because the core of the service is to provide some sort of functionality that might otherwise be provided with installed and licensed software, or just run on the desktop. WAX apps are more than just brochures on the web, and even content management systems don't really qualify. This isn't about publishing (unless its a publishing app, of course) - its about online software.

eXchange - because the online nature of the service works best and makes the most sense when it allows the user to collaborate or otherwise interact with other users who are also online. An online app that allows you to do something in isolation might as well be a desktop app. What makes an online spreadsheet or word processing document WAX is when it can be shared by people.



Where is WAX? Where are some good examples of WAXware?

Well, I was playing with Google Calendar and a beta of something called Spanning Sync. Now, Google has calendaring software which they happen to expose through a cool AJAX web interface. However, they also expose it through an open calendaring data exchange format called iCalendar. On my Mac I could subscribe to the iCalendar interface using iCal (the mac OS X calendar program) and see my Google calendar. However this is read-only.

Spanning Sync takes it a step further, by offering bidirectional sync between iCal and Google Calendar. Now if I enter an appointment in iCal it gets pushed up to Google. But wait, what if I'm sharing my Google calendar. Then someone else could be using it, also using Spanning Sync and iCal.

Suddenly we're collaborating on the web, using Google Calendar. The only thing is - neither of us are actually going to the Google web site! Is it really a web application now? Well, not exactly - its really WAX.

A couple of implications of this idea:

On a business level, it does raise some questions about what models are sustainable. An advertising supported model doesn't work when no one goes to the website to see the ads. The advertising model is why you can launch a Google search from anywhere (from the search bar in FireFox, for example), but you have to view the results on a Google search result web page. That's because that's where the ads are. Those search results will not be available via XML or JSON or whatever, so long as Google is working with their current business model. This implies that if WAX really takes off we'll see more transaction-fee and subscription based business models.

On a technical level the concern with these kinds of integrations is that it creates dependency on a third party to deliver the service. In reality businesses and individuals make this compromise all the time - we rely on third parties for power, banking, payroll, HR, and so on, but this is a new area, so there's reasonable grounds to pause before putting one's mission critical systems in the hands of a startup "Web 2.0" company.

However, my prediction is these issues will be worked out sufficiently over time. WAX will allow businesses and application developers to collaboratively harness each others' value, and should over time reduce the cost and complexity of developing sophisticated online applications.

How To Doc

Most of what you'll find in the "Agile" development methodologies is just common sense. Anyone who's been in development long enough would agree with most of the principles that the Agilists propose. One of the main areas of real controversy has to do with documentation.

This mostly comes out of a reaction to the heavy, "high-ceremony" methodologies of the 90's (like Rational Unified Process - designed to sell Rational tools). The Agile folks essentially have dispensed with the UML, the use cases and so forth and gone with the lightest-weight documentation they can get away with, in pursuit of the idea that they should be building software, not documentation.

Well, for companies like AI where 99% of what we build is for our clients, this "no-documentation" doesn't quite work. This is because:


  • Documentation is part of the approval cycle, in which our clients let us know if we're building the right stuff for them.

  • Documentation lets our clients know what we're going to build before we actually build it.


To fill this gap, some of the Agile methodologies, notably Extreme Programming (XP) recommend an "in-house customer". This sounds both great and horrible to me for different reasons, but in any case I've never known any shop that's actually been able to pull if off. Our customers are to busy doing other things like, say, running their own business, to sit around in our shop being our resident customer. That means we need to document.

At the same time, we certainly don't want to go back to the bad old days of high-ceremony. So we've endeavored to create a light-weight documentation system built on the attributes of the part of the system being developed.

We have 4 documentation types:

Specifications: Written technical specifications that describe the system, either in paragraphs of text or some times with tables that define data structures.

Wireframes: Simple mock-ups of user interfaces that show the layout of the application to the customer.

Prototypes: Simple, interactive versions of the application which demonstrate the interactivity of the app to the user.

Nothing!: Not everything needs documentation! Sometimes the best approach to document is not not document at all.

These four "directions" form our compass:



(Well - its almost N,S,E,W - instead we have N,S,P,W).

When deciding how do document a feature or part of an application, we'll want to look at the following criteria:

Linearity: Is this a serial, step-by-step kind of process we're describing, or is it something that can wander in any direction?

Visibility: Is this a part of the application that is reflected in the UI? Is it something that the user is going to see, or is it strictly behind the scenes?

Interactivity: How interactive is this feature? Is there a lot of back and forth with the user, or does the user just fire off one event and a bunch of stuff happens without further input?

Uniqueness: How unusual is this feature? Is this something that is unique to this app, or unusual in this business space, or is it something that everyone has seen a thousand times before?

These four attributes influence our documentation decisions:

Linearity



The more linear a process is, the more it drives documentation towards a specification. Specifications are particularly good for describing automated, back-end processes. They are less useful for non-linear processes.

Visibility



Visibility in a feature is a good indication that wireframes or prototypes should be used. This is because everyone, including customers and developers, needs to be able to visualize the user interface for the feature.

Interactivity



Interactivity drives towards prototyping. The more back and forth a feature presents with a user, the more inefficient a wireframe is at properly representing it. Instead we need to resort to prototypes - interactive but "hollow" versions of the app that simulate the back-and-forth the real application would provide. This allows the observer to properly understand the interactivity of the feature.

Uniqueness



Unique or notable features require documentation, common and already well-understood ones do not. One option that is always available is to do nothing: to not document the feature. I believe that the driving force here is uniqueness - how notable a feature is. Conversely, common or well-understood features may not require documentation at all.

So how do we use these driving forces? Lets look at a couple of examples.

Lets say our first feature is a complex set of preferences. Choices made by a user may cascade into more choices, and may spawn dialog boxes. Looking at this feature, we can state that it is Visible, Interactive, Nonlinear and Unique. Putting this characterization against our model above, gives us something like the following:



The model has spoken: a prototype of the feature is the best way to document it.

For a very different case, lets say that we have a common print function occurring in the application. Well, everyone has pretty much knows what printing means, so lets call it Invisible, Linear, Non-interactive and Common.



Applying the model, we can see the best thing to do with this feature is to leave it undocumented, everyone already understands it.



Labels: