# Thursday, 25 February 2010

The eXtreme Programming Portland user group, XPDX, is going through some changes. Several members want to change the name to something like the Portland Agile Developers Group to reflect the group's openness to a variety of development methodologies.

I happen to love XP and have used it since 1999 as a general framework and reminder of key principles to keep in mind on any software project. XP, Scrum, Kanban, and every other agile development methodology I've researched have so much in common that I felt it was easier and more pragmatic to just stick with a single name rather than appear to be chasing the latest fad.

But Agile has become a very academic topic and apparently the minor differences between the various methodologies are not to be dismissed lightly.

This forced me to reflect on why I, and many many other people, were initially attracted to XP and why interest has waned. The answer suddenly became very obvious to me.

eXtreme Programming, at one time, promoted "extreme" ideas that challenged the status quo. It was easy to fill a room with 50+ people who wanted a revolution. And the revolution was successful. Most XP best practices are no longer "extreme". We take many XP and Agile practices for granted now. In fact, Agile is now the new status quo!

It's actually pretty boring to attend or present at an agile group meeting and rehash the same ideas. Attendance is way down. So, if changing a group name is a branding effort to increase attendance, I don't believe that will succeed. Sure, it may draw 3-5 more people like an "Under New Management" sign outside an old restaurant, but it won't change the fact that the same old stuff is on the menu.

An aura of infantilism is creeping into Agile. Agile Coaches and Practitoners now seem more like parents than mentors. Agile meeting topics are increasingly about games and tactics that have short term productivity gains of bringing teams around a single purpose. When I ask presenters about the longevity of their agile teams or the software they produced, it's rare to encounter an answer over 6 months, leading me to believe many agile projects are getting by on the initial "honeymoon" inertia before reality sinks in. All successful software projects are ultimately faced with what seems like a daunting and unachievable task that puts tremendous pressure on the team. What a team does at that inflection point is the true test of "agility".

In many cases, Agile practitioners don't even code or have not evolved their skills beyond 1999 object oriented programming techniques. If practitioners spent more time coding, they would have a sense of the new revolution and extreme ideas that are brewing.

OK. So that's out of the way. My intent is not to push people away, but to pull them towards a new way of thinking. I titled this post to promote forward thinking, so let's switch gears.

If XP was extreme in 2000 by virtue of challenging the status quo, then what is extreme in 2010? Many of the following ideas may be tough to accept. If any of these ideas make you feel uncomfortable, threatened or obsolete, then you may be part of the current status quo. But trust me, these ideas are getting traction and are not going away. The sooner we as a development community embrace these changes, the more relevant our meetings will become.

"Feature Democratization"
XPs core tenet of achieving customer satisfaction through customer driven feature stories has evolved in 2010 to make use of idea voting sites. The customer is now the product manager and has become very involved with providing ideas, feature requests, and UI mockups.

"The 24 Hour Iteration"
The recent launch of Google Buzz had millions of people up in arms over privacy concerns while others applauded the new feature. Google had to act fast and update the software within 24 hours in response to the first wave of customer feedback. This was not a "patch". This was an extremely compressed iteration backed by a process that supports this kind of change. Developing in the cloud is living on the extreme.

"Writers Write"
Software Development is not a 9 to 5 job. Many people go home then build websites for their churches, contribute to open source projects, or just simply enjoy programming over the weekends. A personal growth goal for some Developers is to develop their own style, much like a literary writer. The days of seeking validation from a venture capitalist to pursue an idea are over. We don't all need to conform to a universal set of rules or conventions when writing software.

"No Outsourcing"
Great software development projects are not outsourced. No more than a great video game title, movie, song, or book gets outsourced. Let's get over the false idea that software development is an easily outsourced task and embrace the uniqueness and originality any person or team can contribute.

"No SQL"
Many greenfield projects are breaking away from the long history of using relational data stores. The NoSQL movement is developing applications faster and more scalable by sacrificing relational features like JOIN. Many NoSQL projects have characteristics similar to early object oriented database solutions.

"All Javascript All The Time"
A little Javascript in a product like Google Maps can go a long way. Taking this to the extreme and developing entire products in Javascript, such as Google Apps and Microsoft's upcoming Web Office 2010, requires a different approach to software development. New layers of abstraction like GWT, Closure, and JQuery should be explored and embraced by anyone working on web applications.

"See The Forest For The Trees"
Really not an extreme idea, but one I think has been lost. When agile discussions quickly devolve into focusing on a small detail and make an academic issue out of the process, the survivalist in me says "Yeah that's great, but let's not lose sight of the big picture". Maybe the "big picture" just can't be coached. A good team needs to internalize the value being received by the customers and, ideally, be a consumer themselves of what they're developing. How do people discover software? What compels them to try it? What is the install/config process like? Why would they stay engaged? Can game mechanics can be added? How can the customer be given a voice and opportunity to participate in the evolution of the software? For agile teams, this process is "fun". For other teams, the Developers actually need to be told what to do, how to do it, and when to deliver it.

"Work from home / Distributed collaboration"
The essence of pair programming is to collaborate with other Developers on a single task. In reality, most software is not written with 2 Developers physically in the same space. The Internet allows for more distributed collaboration opportunities in 2010. How are successful projects taking this to an extreme and using this to their advantage?

"Quit Unit Testing and Write Better Code"
Unit testing is largely used in object oriented projects as a check on the side effects and loose contracts inherent in OOP. Design by Contract and Functional Programming have evolved (actually FP has been around quite awhile) to address these quality issues up front. One extreme idea is to "not do unit testing" and actually improve the quality of code by removing side effects with functional programming and using design by contract for runtime enforcement. Several agile projects with tons of unit tests just end up being "well tested crap". What's wrong? Unit testing is not a silver bullet. Let's get that out on the table.

"Pair Programming Sucks"
Let's face it. When 2 people with compatible coding and social skills work together, they'll produce something well beyond what they'd create on their own. If the pair is not compatible, then the code will suffer and they'll possibly make life worse for others around them. You can't "coach" people to work together. Dogmatic pairing of programmers cannot be universally applied to all projects. Good teams and collaborations take time to develop.

"Develop In The Cloud"
Do you really need to spend an iteration "0" setting up servers, getting everyone's desktop on the same version, and installing a database? Once the project goes live do you want to take a 3am call to address a load balancing bug? If you encounter any measure of success, are you really prepared to scale and meet the demand of that success? The status quo says "Yes, how else will get ROI from the datacenter we just built?". The extreme programmer would say "No way. Writers write. You handle the rest".

"Federated Cloud"
We've all been conditioned to avoid "Vendor lock-in" when making a significant investment in IT infrastructure, but are we making the same mistakes as more software development moves to the cloud? Will all software eventually run on one of 4 large datacenters? The cloud was built on open source. Now the open source movement needs to be revitalized in the cloud, move beyond HTTP/SMTP, and federate a more abstract layer on the Internet using open source.

"Bootstrap to Success"
How relevant is a Computer Science degree or VC funding in your decision to using software? Anyone can develop great software and become financially independent using proven bootstrapping techniques and the Internet as a distribution channel.

I've itemized just over a dozen extreme ideas here. There are many more. Do XP groups need re-branding or does XP itself need to be redefined to take on new extremes? Should we recalibrate and invoke XP's FixIt rule?

If even one other person in Portland wants to discuss these ideas, I'd be happy to meet over a Beer (or two) to explore the "new extremes" on a regular basis.

Comments? Ideas? Suggestions?

Thursday, 25 February 2010 12:08:06 (Pacific Standard Time, UTC-08:00)
# Monday, 22 February 2010

I'm presenting "Developing In The Cloud" at the Portland JavaScript Admirers Group this month. If you're in Portland this Wednesday, February 24th, 2010, come check it out 7pm-10pm!

The talking points are below. There'll be several visual examples and live coding demonstrations.

Monday, 22 February 2010 10:40:29 (Pacific Standard Time, UTC-08:00)
# Sunday, 21 February 2010

There are several bank specific rules for validating credit cards, but all card numbers can be validated using the Luhn algorithm. The Luhn algorithm, aka the Luhn formula or "modulus 10" algorithm, is a simple checksum formula used to validate a variety of identification numbers.

It is not intended to be a cryptographically secure hash function; it was designed to protect against accidental errors, not malicious attacks. Most credit cards and many government identification numbers use the algorithm as a simple method of distinguishing valid numbers from collections of random digits.

Programmers accustomed to using '%' as a modulus operator need only to shift gears slightly and use the Math.mod(int1, int2) library function when working with Apex.

public static boolean isValidCheckSum(List<integer> numbers){
	integer sum = 0;
	integer len = numbers.size();
	
	for (integer i = len - 1; i >= 0; i--)
	{
		if (math.mod(i , 2) == math.mod(len,  2) )
		{
			integer n = numbers[i] * 2;
			sum += (n / 10) + ( math.mod(n, 10));
		}
		else
			sum += numbers[i];
	}
	return ( math.mod( sum, 10) == 0 );
}
Sunday, 21 February 2010 12:15:00 (Pacific Standard Time, UTC-08:00)
# Tuesday, 16 February 2010

I spent a few hours tackling this problem, so hopefully this blog post will spare someone else the difficulty of using Salesforce Spring '10 features before it is broadly released.

Spring '10, aka version 18, is a big release for Salesforce Developers. The current version of Eclipse Force IDE defaults to using the version 16 API.

Once Spring '10 was installed on my org (NA1) I committed to using the v18 features on a couple new projects. Unfortunately the Spring '10 release hit a snag, so now the release is being staggered over 30-45 days and the new Force IDE release has been put on hold.

Fortunately, there are a couple workarounds to using the v18 API and features today.

Option 1)
a) Create an Apex class as you normally would in Eclipse and accept version 16 as the default.
b) Eclipse will create a second file next to the class with a 'meta.xml' extension.
c) Edit the -meta.xml file by changing the version to 18 then save.

Option 2)
a) Create an Apex class through the Salesforce browser interface but do not accept the default version 18. Instead, select version 16.
b) Open the Salesforce project in Eclipse and confirm the Apex class is available in the IDE (the current IDE apparently won't automatically sync with classes > v16. At least that was my experience).
c) Back in the Salesforce browser, change the version from 16 to 18.

Finally, depending on which approach used, you'll need to synchronize the Eclipse IDE with Salesforce servers using either the "Deploy to" or "Refresh from" server options by right clicking on the class and selecting from the Force.com option. (Option 1 requires "Deploy to". Option 2 "Refresh from").

Hope this helps!

Tuesday, 16 February 2010 18:28:19 (Pacific Standard Time, UTC-08:00)
# Tuesday, 02 February 2010

Every once in awhile I run into a requirement when using Apex where I think there must be a real obvious way to do something, only to hit a wall and decide to develop the functionality myself.

When this happens, the result either ends up being:
a) I later discover there actually is a faster/better way to do something and I feel stupid for taking the NIH path (Not Invented Here).
b) The functionality really is missing and the effort invested turns out to be well spent.

Let's just hope the time spent on this wrapper class is in the latter category :-)

In this use case, a custom database object is wrapped in an Apex class that abstracts the underlying object properties and provides some methods. One of the properties (a database custom field) is a multipicklist that is stored in semicolon delimited format, but is better represented in object-oriented terms as a List. Methods for Adding and Removing items from the list are also needed.

public class Foo{
	private Foo__c m_record = null;
	public Foo(Foo__c record){
		m_record = record;
	}

	private MultiSelectProperty m_categories = new MultiSelectProperty();
	public List<string> Categories{
		get{return m_categories.ToList(m_record.Category__c);}
	}
	public void AddCategory(string category){
		m_record.Category__c = m_categories.Add(m_record.Category__c, category);
	}
	public void RemoveCategory(string category){
		m_record.Category__c = m_categories.Remove(m_record.Category__c, category);
	}
}

The MultiSelectProperty class is defined here. The unit tests should help tell the story on how it can be used and the assumptions it makes.

//Manages semicolon-delimited multipicklist data. 
global class MultiSelectProperty {
	
	public List<string> ToList(string storedValue){
		List<string> values = new List<string>();
		if(storedValue == null || storedValue == '')
			return values;
		else{			
			string[] splitValues = storedValue.split(';');
			for(string v : splitValues){
				if(v != null && v != '')
					values.add(v);
			}
			return values;
		}
	}
	
	public boolean Contains(string field, string value){
		List<string> values = this.ToList(field);
		for(string v : values){
			if(v==value){
				return true;
			}
		}
		return false;
	}
	
	public string Add(string field, string value){
		if(field == null)
			field = '';
		
		if(value == null || value == '')
			return '';
		
		if(this.Contains(field, value))
			return field;
		
		if(field.endsWith(';')){
			return field + value;
		}
		else
			return field + ';' + value;
	}
	
	public string Remove(string field, string value){
		if(field == null || field == '')
			return '';
		
		if(value == null || value == '')
			return field;
		
		if(!this.Contains(field, value))
			return field;
		
		List<string> values = this.ToList(field);
		string formattedFields = '';
		for(string v : values){
			if(v == value)
				continue;
			formattedFields += v + ';'; 
		}
		return formattedFields;		
	}
	
	@IsTest public static void tests(){
		final string SINGLE_VALUE_NULL = null;
		final string SINGLE_VALUE_EMPTY = '';
		final string SINGLE_VALUE = 'first option;';
		final string SINGLE_VALUE_EMPTY_TRAIL = 'first option';
		final string DOUBLE_VALUE = 'first option;second option;';
		final string TRIPLE_VALUE = 'first option;second option;third option;';
		
		MultiSelectProperty categories = new MultiSelectProperty();
		System.assert(categories.ToList(SINGLE_VALUE_NULL).size() == 0);
		System.assert(categories.ToList(SINGLE_VALUE_EMPTY).size() == 0);
		System.assert(categories.ToList(SINGLE_VALUE).size() == 1);
		System.assert(categories.ToList(SINGLE_VALUE_EMPTY_TRAIL).size() == 1);
		System.assert(categories.ToList(DOUBLE_VALUE).size() == 2);
		System.assert(categories.ToList(TRIPLE_VALUE).size() == 3);
		
		System.assert(categories.Contains(SINGLE_VALUE, 'first option') == true);
		System.assert(categories.Contains(SINGLE_VALUE, 'second option') == false);
		System.assert(categories.Contains(DOUBLE_VALUE, 'second option') == true);
		System.assert(categories.Contains(DOUBLE_VALUE, 'third option') == false);
		System.assert(categories.Contains(TRIPLE_VALUE, 'first option') == true);
		System.assert(categories.Contains(TRIPLE_VALUE, 'second option') == true);
		System.assert(categories.Contains(TRIPLE_VALUE, 'third option') == true);
		
		string fields = categories.Add(SINGLE_VALUE, null);	
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(null, null);
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(null, '');
		System.assert(categories.ToList(fields).size() == 0);
		fields = categories.Add(SINGLE_VALUE, '');
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Add(SINGLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Add(SINGLE_VALUE_EMPTY_TRAIL, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Add(DOUBLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'second option') == true);
		
		fields = categories.Remove(null, '');
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Remove(null, null);
		System.assert(categories.ToList(fields).size() == 0);
		
		fields = categories.Remove(SINGLE_VALUE, '');		
		System.assert(categories.ToList(fields).size() == 1);
		
		fields = categories.Remove(SINGLE_VALUE, null);		
		System.assert(categories.ToList(fields).size() == 1);
		
		fields = categories.Remove(SINGLE_VALUE, 'second option');
		System.assert(categories.ToList(fields).size() == 1);
		System.assert(categories.Contains(fields, 'second option') == false);
		
		fields = categories.Remove(SINGLE_VALUE, 'first option');
		System.assert(categories.ToList(fields).size() == 0);
		System.assert(categories.Contains(fields, 'first option') == false);
		
		fields = categories.Remove(TRIPLE_VALUE, 'first option');
		System.debug('results from remove ' + fields);
		System.assert(categories.ToList(fields).size() == 2);
		System.assert(categories.Contains(fields, 'first option') == false);
		System.assert(categories.Contains(fields, 'second option') == true);
		System.assert(categories.Contains(fields, 'third option') == true);
	}
}
Tuesday, 02 February 2010 12:23:21 (Pacific Standard Time, UTC-08:00)