Introducing Code Usability
Usability guidelines can sometimes be of use in awkward places. I try to apply them to source code.
The sfBlogs source code is published under an open-source license, and its main purpose is to be reused by other developers. Consider the source code as an interface, and developers as end-users of this interface. There, usability doesn't sound that awkward anymore, does it?
Of course, coding guidelines are there to make the code easy to read by everyone. But code usability goes somehow beyond. Let's see some of the differences.
Bad Code Comments
Documentation is sometimes overkill - provided that you name your classes and methods properly. For instance, code sample #1 is way shorter than code sample #2, but a developer can understand (and adapt) its code more easily.
// code sample 1
class sfBlogLogger
{
/**
* Log a blog creation
* @param sfBlog $blog The blog just created
* @param sfGuardUser $author The user who created the blog
*/
public static function create($blog, $author)
{
$log = new sfBlogLog();
$log->setElements($author, $author, 'create_blog', $blog, 'New blog: "%object%"');
$log->setObjectLink('sfBlogAdmin/blogEdit?id='.$blog->getId());
$log->save();
}
}
// code sample 2
/**
* myClass is there to log events happenning on a blog
* I built this class because when I breate a blog,
* I want a new record to be created in the sfBlogLog model
* So the class does it automatically, with static classes and all
*
* @author me
* @email me@myaddress
* @date 04/23/09
*
* @see sfBlogLog
*/
class myClass
{
/**
* Log a blog creation
* This method creates an sfBlogLog object, asets is Elements based on the blog and author,
* Sets the object link, and saves to the database
*
* Example:
* myClass::log1($b, $a)
*
* @param sfBlog $b The blog just created
* @param sfGuardUser $a The user who created the blog
*
* @author me
* @email me@myaddress
* @date 04/23/09
*
*/
public static function log1($b, $a)
{
$l = new sfBlogLog();
// pass the author and the blog to the log object
$l->setElements($a, $a, 'create_blog', $b, 'New blog: "%object%"');
// set the link on the log object
$l->setObjectLink('sfBlogAdmin/blogEdit?id='.$b->getId());
// save to the database
$l->save();
// return nothing
}
}
With too many comment lines, a developer cannot see the whole class in the screen without scrolling, and that can be harmful to comprehension. Excess of comments is not as seldom as one might think. In fact, comments are often required to balance a poor code design or naming. Use the comments for what they are good for (code completion, non-trivial usage), and let the code speak by itself.
Split Up Code
Sometimes code smells bad. Experienced developers can recognize at first glance stinking pieces of code (too many indentation levels, too long files, too many method arguments, poor object-orientation, etc.). But making your code smell good is not enough to make it usable. For instance, breaking down a large file into smaller ones can make the code hard to read because it forces the developer to switch between files to get the whole picture. So you may need to keep a gentle scent to keep the code usable.
For instance, the new symfony routing system allows for very concise action methods:
/**
* Displays a list of blogs
* Required request parameters:
* page Page number (defauls to 1)
*/
public function executeBlogs($request)
{
$this->blog_pager = $this->getRoute()->getObjectsPager(
$request->getParameter('page', 1),
sfConfig::get('app_sfBlogs_post_max_per_page', 5)
);
}
However, in order to get the big picture, you must look at the routing rule in the application's `routing.yml`:
blogs:
class: DbFinderObjectsRoute
options: { model: sfBlog, finder_methods: [recent] }
url: /blogs/all/*
params: { module: sfBlog, action: blogs }
I'm not sure that the two pieces of code above are more usable than this one:
/**
* Displays a list of blogs
* Required request parameters:
* page Page number (defauls to 1)
*/
public function executeBlogs($request)
{
$this->blog_pager = DbFinder::from('sfBlog')->
recent()->
paginate(
$request->getParameter('page', 1),
sfConfig::get('app_sfBlogs_post_max_per_page', 5)
);
}
I have implemented a set of powerful routing objects in `DbFinder`, the Model requesting API I've been working on for quite some time now, but I'm not convinced that they make the code more usable.
Cleanliness
If a little code smell is sometimes better than a strict adherence to coding standards, keeping the code clean is compulsory to make it usable. Clean indentation, not too long lines, and coherent naming conventions, are necessary to avoid bad surprises for the developers who read your code.
New Conventions
If a convention exists for a particular task, you should use it, rather than introduce your own. Relying on existing knowledge eases the learning process, since there are less things to learn. "Do Not Reinvent The Wheel" is also a usability guideline.
When designing the DbFinder API, I tried to use as much as possible method names that would feel natural to developers with a basic SQL knowledge. The two following code snippets to the same thing, but the second one requires much less knowledge to be understood, and adapted.
// With Propel Peer and Criteria
$c = new Criteria()
$c->add(ArticlePeer::TITLE, '%world', Criteria::LIKE);
$c->add(ArticlePeer::IS_PUBLISHED, true);
$c->addAscendingOrderByColumn(ArticlePeer::CREATED_AT);
$articles = ArticlePeer::doSelectJoinCategory($c);
// with DbFinder
$articles = DbFinder::from('Article')->
where('Title', 'like', '%world')->
where('IsPublished', true)->
orderBy('CreatedAt')->
with('Category')->
find();
Listen To User Feedback
Usability is not so much about a set of rules as it is about a way to constantly judge your work with the end user in mind. And the end user is not an abstract human being - at least, not after you release your product. Open source licenses favor user feedback, and that feedback is the precious ingredient you need to evolve towards a better usability. So a good rule of thumb for an open-source component that favors usability is to provide easy ways for developers to send feedback, feature requests or simple questions. It is also crucial to actually listen to the developers comments, and to take them into account for the future.
By the way, if you tested the sfBlogs plugin, please send me an email to tell me your impressions. Your feedback is really necessary for this experiment to go in the right direction.
Conclusion
I've barely scratched the concept of code usability in this post; if you want to know more, you can read a presentation I made a few months ago on the same subject: Developing For Developers.