Archive for November, 2009
Flex Application View Organization
The way I develop software regularly evolves as I learn better practices, and this is particularly true in the way I organize view code in Flex applications. I've seen (and built) too many applications where the code is unncessarily difficult to jump into and maintain. Inspired by domain-driven design and test-driven development, I've been organizing view code in such way that I believe helps maintainability and also helps answer the question, "Where should I put this component?".
Here are some guiding principles:
- The view code should reflect how you talk about the application with the entire team.
- The view code should reflect how you interact with the application.
- Repeating yourself can be a good thing.
- Components and events should be contextual.
Let's way we have an application that manages contacts. There's a list of all your contacts on the left. There's a list of recent contacts on right right. The selected contact's detail shows up in the middle. There is also a Twitter feed widget in the contact's details. Here's a wireframe:

Here's how I would create the view package:
com.foomonger.contactmanager.view.ContactDetailsView
com.foomonger.contactmanager.view.contactdetails.events.ContactDetailsEvent
com.foomonger.contactmanager.view.contactdetails.TwitterFeedWidget
com.foomonger.contactmanager.view.AllContactsView
com.foomonger.contactmanager.view.allcontacts.ContactItemRenderer
com.foomonger.contactmanager.view.RecentContactsView
com.foomonger.contactmanager.view.recentcontacts.ContactItemRenderer
So what did I do? I created components that reflected the wireframe. There is a component for each major section (all contacts, contact details, recent contacts). I named the Twitter component "TwitterFeedWidget" even though I thought the "widget" part was dumb but that's what the product owner and overall team calls it. I also created a ContactDetailsEvent class within an "events" package specific to the ContactDetailsView component. That may be a bit extreme for some people, but I find that it helps me keep track of the purpose and scope of view events. I also created a ContactItemRenderer for both the AllContactsView and RecentContactsView. Even if the code was identical in each item renderer, I prefer to keep them distinct instead of having a common one in a generic "itemrenderer" package. I argue that what I lose by repeating myself I gain back in maintainability. If there was a common ContactItemRenderer, after many months of new feature development with several developers it'll be easy to lose track where it's used. If the item renderer has to do something different in one particular case you can of course add properties and states to keep it multipurpose. However I've found this to be regression prone. If you have distinct contextualized components, you can be more confident that a change will not cause regression elsewhere.
I used to always look for even the slightest resemblance of a pattern and abstract all kinds of code into shared components. But I've realized that developing an application is different from developing a component library or a framework. In a component library you can have a generic color picker. In a framework you can have a generic "itemrenderer" package. I'm not saying you can never do these things in an application, but I am saying that I think application development should be very (domain) specific.
Agile Refactoring
There's always something to refactor. Requirements evolve, short-cuts are taken, code doesn't get reviewed, and anti-patterns emerge. How does refactoring fit into agile development? How do you have to convince the product owner to make a refactoring system story a high priority for the next sprint? How do you address that crumbing design pattern a random contractor setup years ago? Here are some practices to help address these issues:
Document areas to refactor. When you have to take a short-cut to hit a deadline, first of all comment the code and then create a ticket to address the short-cut in another sprint. I also suggest tagging the ticket as "technical debt." This will give your team visibility into the amount of technical debt that is accrued.
Include refactoring into story estimations. If you know that a story would be best implemented if you refactored some code, don't start off by giving an estimate that assumes you'll use a short-cut. It can be natural to people-please and say things can be done sooner but try to avoid that and keep in the mind the goal of writing high quality software which includes refactoring.
Clean up the small things when you see them. Refactoring doesn't have to be a clean sweep of the whole house, it can be just a good habit of picking up things along the way. I wouldn't recommend rewriting significant logic for fun, but if you're in some code and find that a minor change would significantly improve readability then it might be worth the few minutes to do the cleanup.
Break down large refactoring changes into manageable tasks. You'll never have the time to do that gigantic overhaul of that terrible reporting system (or what-have-you) all at once. What you can do is break it down so that the work can be spread out across many sprints. This effort can be complex so I suggest starting by creating a task to outline all the changes that should be made.
Evaluate and convey the business value of the refactoring need. It's likely difficult to find a product owner who will add a refactoring system story into a sprint just because you tell them it'll make the code better. There's no apparent business value in that reason. Think of the issue from the product owner's perspective and consider the "so that" part of the system story. Ask yourself, "Would it improve system performance? lower technical debt? improve throughput? address the root causes of the growing list of bugs?" You may even find that the refactoring you wanted to do doesn't have significant business value and is just clean and maintainable enough the way it is.
One of the great things about agile development is the exposure and mitigation of risk. If a system needs significant refactoring then that's risk that the entire team should be mindful of. Refactoring can then be something that happens, not just something you want to do.