~ flex ~

18
Oct
2007

The dark side of software as a service

During the 80s, Sun developed a networked filesystem and it became kind of a fad to use dumb-ish Unix workstations with all of your information stored on the network. At the time, I remember someone describing this trend by saying “it’s like using a normal computer, except that every once in a while, a server you’ve never heard of goes down, and you can’t access your files anymore”.

Fast forward to 2007. I happen to have multiple computers at home, so I’ve been using Google Docs to store some of my critical information so that I don’t have to worry about which computer I’m using. And.. guess what? Google Docs is completely hosed right now (at least for me).

It’s a weird feeling to not be able to access my own data until someone else fixes their problem. Maybe I should have thought about that before putting my data in the cloud…

1
Oct
2007

Quick analysis of Adobe and Buzzword

You may have already heard about Adobe’s acquisition of Virtual Ubiquity, the company that makes Buzzword. Being an Adobe fan and shareholder, I am excited. Rick Treitman and his gang are a smart, experienced group, and the software is simply amazing.

That having been said, I have a number of concerns. Maybe concerns is too strong. Let’s put it this way. I have some hopes about how Adobe goes about this.

I hope they find a way to make Buzzword available freely to everyone instead of just trying to sell to corporations. There was a bank robber by the name of Willie Sutton, who when asked why he robbed banks, replied: “That’s where the money is.” In a sense, this is why companies try to sell software to large corporations.

The most likely scenario that I can imagine is that they try to put Buzzword together with Acrobat and their newly announced Share product into a sort of office suite that they sell at relatively low cost (~5k?) to corporations. Why do I worry about this?

  1. Online applications are a potentially disruptive technology, but only when combined with easy availability. Look at the inroads that Google docs has already made. That application looks like a toy compared with Buzzword (no offense to Sam and Steve and the rest of the Google docs gang). But if Buzzword becomes a behind-the-firewall application that only large enterprises purchase, it won’t get the momentum it needs to move forward.
  2. In the enterprise, a better product is not necessarily the most important thing, and Adobe is not yet a company that has a proven track record of selling to enterprises. That’s not to say that Adobe couldn’t get there someday, but it’s hard.
  3. I’m just speculating about pricing, but a mid-priced (5k?) product is a very difficult product to sell. It’s not expensive enough to warrant a salesforce, but it’s too expensive for people to try out and adopt virally.

In general, I am usually skeptical of freemium models, but in this case, I think it’s the right one. (free product + premium offerings + expensive software sales for enterprises).

I hope Adobe doesn’t stretch itself too thin by trying to do too many things. Let’s count the fundamental shifts that Adobe is trying to undergo, all at the same time:

  1. Become a platform company.
  2. Become a technology provider to the mobile industry.
  3. Become an enterprise software company.
  4. Become a company that delivers software as a service.

Any one of these is potentially transformative, and very difficult to achieve. Trying to do it all is very scary. Maybe Adobe will pull it off, but I would feel more comfortable if the company was trying to do fewer things.

I hope Adobe finds a way for the Virtual Ubiquity guys to continue working as if they were at a startup. Even when everyone is doing everything right, organizational dynamics tend to change when a smaller company is absorbed inside of a larger one. And this space right now is going to get so competitive that I would rather bet on a small startup than a large company. Or better yet, I hope they can harness the power of both.

~

So those are my hopes. Overall, I am very bullish. Buzzword is an amazing piece of software, and the Virtual Ubiquity team is outstanding. Adobe leadership is smart and determined. The space is undeniably exciting. I am 100% sure that in 10 years, the way that people use productivity software will have completely changed. So Adobe is getting into a very exciting game with a great product. There is a lot of competition out there, and Adobe has a number of challenges ahead. I hope they come out on top.

17
Sep
2007

Leaving Adobe

Hi folks.

As it turns out, I have decided to leave Adobe.

The reasons are complicated, but my decision was somewhat sudden and unexpected, so I am not quite sure what I’ll be doing next. If you have any ideas for me, let me know! For the time being, I won’t be posting much until I figure out what I’ll be doing.

Thanks to all the great people I met along the way. It has been tremendous fun. I will be rooting for Flex from the sidelines.

17
May
2007

Introducing the FlexibleContainer component (aka Advanced ActionScript Refactoring – Step 3)

We’re now ready for our final step. We started with something that looked like this, and we are going to end up with something that looks like this. As you can see, the layout of the container is assignable at runtime.

After step 2, there was very little refactoring to do. I removed the DragTile class once and for all, and I added styles back in. The way that the Flex styling mechanism works, you can implement styles for non UIComponents, and that’s what I did. Each Layout object can have its own styles.

I created another concrete layout called CircleLayout to make sure that the layout was actually assignable at runtime. This exposed a few bugs, which I fixed. I factored out the common code between TileLayout and CircleLayout into a common concrete superclass, called Layout. In the end, the inheritance hierarchy for the layout managers looks like this:

I also “fixed” some nitpicky things along the way. I’ve always thought that the drag/drop feedback was a bit confusing, so I changed the behavior in a subtle way (see if you can spot it!).

In the end, the true test of whether this refactoring worked is to see how easy it is to add a new layout. Whereas the original DragTile code was 600+ lines of code, the CircleLayout code is only about 100 lines of code, and all of it pertains to geometry. There is virtually no code in there to manage renderers or animators or anything like that.

public class CircleLayout extends Layout
{
// ILayout interface		
override public function getMeasuredSize():Point
{
	return getMaxSize();
}

override public function findItemAt(px:Number, py:Number, seamAligned:Boolean):Number
{
	// Can't execute this if we aren't attached to a container.
	if (!container || container.renderers.length == 0)
		return NaN;
		
	// Get the radius and center of the circle.
	var radius : Number = Math.min(unscaledContainerWidth, unscaledContainerHeight) / 2;
	var hCenter : Number = unscaledContainerWidth / 2;
	var vCenter : Number = unscaledContainerHeight / 2;
	
	var angle : Number = Math.atan2(py-vCenter, px-hCenter);
	if (angle < 0)
		angle += 2 * Math.PI;

	// figure out the closest "item" by working backwards from the angle to the index, using floating point math.
	var result : Number = container.renderers.length * angle / (2 * Math.PI);

	// depending on whether this is seam aligned, do a ceil or round.			
	result = (seamAligned) ? Math.ceil(result) : Math.round(result);
	
	// do a modulo op to make sure that this is within [0, length-1]. Modulo is the correct
	// operator in this case because this is a circle.
	result %= container.renderers.length;
	return result;
}

override public function generateLayout():void
{
	// Get the radius and center of the circle.
	var radius : Number = Math.min(unscaledContainerWidth, unscaledContainerHeight) / 2;
	var hCenter : Number = unscaledContainerWidth / 2;
	var vCenter : Number = unscaledContainerHeight / 2;

	// Find the max item size.
	var maxSize : Point = getMaxSize();
	var max : Number = Math.max(maxSize.x, maxSize.y);
	
	// Inset the radius by the max size.
	radius -= max;

	// Loop through the items and position them.
	var length : int = container.renderers.length;
	for (var idx:int = 0; idx < length; idx++)
	{
		var renderer:IUIComponent = container.renderers[idx];
		var target:LayoutTarget = animator.targetFor(renderer);//targets[idx];
		
		// evenly space each item over 2*pi radians.
		var angle : Number = (2 * Math.PI) * idx / length;
		
		// position items on a circle.
		target.scaleX = target.scaleY = 1;
		target.item = renderer;
		target.unscaledWidth = renderer.getExplicitOrMeasuredWidth();
		target.unscaledHeight = renderer.getExplicitOrMeasuredHeight();
		target.x = hCenter + radius * Math.cos(angle) - target.unscaledWidth/2;
		target.y = vCenter + radius * Math.sin(angle) - target.unscaledHeight/2;
		target.animate = true;					
	}
	
	// If there is more than one item, and if there is a drag target, nudge the items next to the drag target
	if (length > 1 && container.dragTargetIndex >= 0 && container.dragTargetIndex < length)
	{
		// Find the items to the left and right of the target.
		var leftIndex : int = (container.dragTargetIndex + length - 1) % length;
		var rightIndex : int = (leftIndex + 1) % length;
		
		var leftTarget : LayoutTarget = animator.targetFor(container.renderers[leftIndex]);
		var rightTarget : LayoutTarget = animator.targetFor(container.renderers[rightIndex]);
		
		// exaggerate the difference between the two targets by a factor of maxSize/2.
		var dx : Number = rightTarget.x - leftTarget.x;
		var dy : Number = rightTarget.y - leftTarget.y;
		var distance : Number = Math.sqrt( dx*dx + dy*dy );
		
		leftTarget.x -= dx / distance * max/2;
		leftTarget.y -= dy / distance * max/2;
		rightTarget.x += dx / distance * max/2;
		rightTarget.y += dy / distance * max/2;
	}
}

protected function getMaxSize() : Point
{
	// Can't execute this if we aren't attached to a container.
	if (!container)
		return new Point(0, 0);
		
	// Find the max item size.
	var maxWidth : Number = 0;
	var maxHeight : Number = 0;
	
	if(container.renderers.length > 0)
	{
		for(var i:int=0;i<container.renderers.length;i++)
		{
			var itemRenderer:IUIComponent = container.renderers[i];
			maxWidth = Math.ceil(Math.max(maxWidth,itemRenderer.getExplicitOrMeasuredWidth()));
			maxHeight = Math.ceil(Math.max(maxHeight,itemRenderer.getExplicitOrMeasuredHeight()));
		}
	}

	return new Point(maxWidth, maxHeight);
}

Could this be improved? Sure. There are still linkages between the layout and the container that we should probably get rid of. But for now, I think I’m going to stop. Maybe I’ll refactor the rest of it away later. :-)

Code for the final version can be found here.

16
May
2007

Advanced ActionScript Refactoring – Step 2

In Step 1, we created a superclass to hold the common logic for all flexible layouts. Now, let’s change the model from inheritance to composition. My goal in this step was to move all DragTile functionality into a separate helper class, leaving DragTile as just an empty shell.

Until now, we have not had to worry about the contract between the container and the layout manager. All we did in step 1 was decide which methods belonged in the superclass and the subclass on a somewhat haphazard basis.

Now that we want to move this into a separate helper object, we need to ask ourselves how the main object (the container) needs to communicate with the helper object. A good starting point is to look at the members that the DragTile subclass defines.

All of the properties (green dots) seem to be specific to DragTile. Meanwhile, three member functions at the bottom look truly general: measure(), findItemAt(), and generateLayout(). Let’s use these as our initial interface for ILayout.

Because measure() is a method defined on IUIComponent, It doesn’t make sense to just move it to a non-UIComponent object. Instead, I decided to define a helper function, getMeasuredSize(), that we call from the measure() function. And thinking through some of the other complexities that might arise, I added an attach() and detach() method so that the layout manager could do some initialization and cleanup whenever it was used. The interface now looks like this:

I now had to make this “real” by creating a layout property of FlexibleContainer and creating a concrete implementation of ILayout called TileLayout.

How do we get from where we are in step 1 to where we need to get to in step 2? In step 1, DragTile is a subclass of FlexibleContainer. After step 2, we know that there won’t be a class called “DragTile” anymore.

This seems like a hard change to make incrementally, and the whole point of refactoring is to do these changes in small, incremental steps. Which brings me to the next tip.

Tip 5: If needed, build temporary scaffolding to make sure your code continues to “work” as you refactor.

In this case, I used both inheritance and delegation while I was in the middle of the refactoring. In other words, I wrote a little bit of scaffolding code to keep the DragTile class around even after I had created the TileLayout class, and slowly moved code over from one to the other.

For those of you who are not familiar with the ideas behind refactoring, this writing of temporary code might seem kind of insane. However, once you get used to it, you will never go back. Big changes are much easier to do successfully in small, bite-sized pieces. Sometimes this means writing a few lines of shim code to hold things up as you change things incrementally.

The next step was to move all the functionality out of the DragTile subclass. During this phase, I didn’t move entire methods. Instead, I dissected methods line by line and moved the correct functionality over to the appropriate class using the following simple rules:

  • If the logic contained in the line of code was “general”, it move it to the FlexibleContainer superclass
  • If the logic contained in the line is specific to tiles, move it to the “TileLayout” class

This process worked fine for most of the logic, but the styles were a different matter. TileLayout is not a UIComponent, so it doesn’t participate in the CSS system. Meanwhile, the styles that DragTile defines are specific to tiles (vGap, hGap, etc). While it is possible to have things that are not UIComponents participate in CSS, it takes a lot of work, and I didn’t feel like tackling it right then.

What I decided to do was to turn off the style functionality temporarily. This brings us to the next tip:

Tip 6: When temporarily disabling functionality during a big refactor, be sure to do so in a way that preserves information, ideally through stub functions.

One way to turn off the CSS functionality would have been to comment out the lines of code that refer to CSS and restore them later. The problem with this approach is that it is difficult to make sure that the the code inside your comments continues to be “correct” as you move your code around.

To help make sure that the CSS knowledge that was embedded in Ely’s code didn’t get lost, I created a stub getStyle() function that always returned NaN. This meant that all of the CSS logic in Ely’s code was still there and still functioning, but it was attached to a CSS style sheet where nothing was ever defined.

By keeping Ely’s code alive, it was easy to make sure that I was making the right edits to it as I refactored. As I moved code around, the compiler would remind me if I was doing something terribly wrong. This doesn’t prevent all coding errors, but it helps prevent many of them.

After all this, I was able to move all of the code from the DragTile code into one of the other two classes. The final version of the DragTile class looked like this:

public class DragTile extends FlexibleContainer
{
	public function DragTile()
	{
		super();
		
		layout = new TileLayout();
	}
}

We are now (obviously) at the point where we can get rid of the DragTile object. Code for step 2 can be found here