An important but oft-overlooked principle of software design is the aggressive culling of unused features. The best software products are slim and lean, with exactly the features its users need and few that they don’t. This like weeding and pruning in your garden: without it, you’ll eventually be overrun.
The types of apps that I most commonly work on are internal applications used by perhaps a few hundred users in a single organization, or across several organizations. You’d think that with such a narrow audience, it would be easy to get information about what’s being used and what isn’t. Not as easy as it seems, though, because the users are not good at analyzing their own use. If you ask them whether a particular report is used, for example, they’ll make vauge noises like “Oh yeah I clicked on that once” or “Huh I hadn’t seen that before, but I’ll definitely use it now that I know about it.” In most cases this stuff is not true, but it’s very hard to tell.
Historically my approach to this has been to cut a feature I think is unused and wait for someone to complain. This works well enough because my intuition is right 90% of the time; but the 10% it is wrong, I can end up with cranky users. (In doing experimental cutting like this, I usually remove the link a few days prior to deleting the connected code. That makes it a cinch to put it back in when necessary.)
A burst of inspiration hit me the other evening. We don’t need to ask the users: the application should be able to track this! To this end, I’ve created Axeman. This is a tiny Rails plugin that tracks usage in a SQLite database, and displays a simple report with the results constrained by time. Screenshot:

In the left column is a traffic report, comparable to a web log analyzer like the venerable AWStats. The Axeman report is way simpler and doesn’t have any fancy graphs, though, so this isn’t too exciting. Besides, you can install AWStats or whatever to get this info about your Rails app. Where it gets more interesting is the right column.
Here we see controller actions that have not been accessed during the selected time period. These are determined by analyzing the source of your app/controllers directory, and cross-referencing it against the usage data.
As an aside, I think it’s interesting that this is only possible because of the structured and convention-based nature of modern application frameworks. Axeman is a very simple example, but I am hoping that as time progresses, we will see more self-aware / self-introspecting application components.
What does it mean when actions appear in the righthand column? Let’s look at the example shown in the screenshot. This is a tiny app and I didn’t expect there to be many dark corners, but as it turns out there’s quite a bit of dead code. First, there’s a bunch of account signup stuff which is unused - this was created by the generator for the login engine. It’s not used, so axe it.
Next, we see that categories and authors both have index actions which are never accessed. Looking at the code I see that these are just redirects to the list action. However, the list action seems to be linked directly, since that one appears in the lefthand column. No need for them then: the axe claims two more victims.
Books has a few unused actions. sort_order is a vestigial remnant of an ajax feature which is no longer used; it goes under the axe. destroy is a working method, but not linked anywhere; most likely created as part of CRUD, but then whoever did the UI didn’t feel that it was needed. We could link it, I suppose, but why bother? If the app has gone this long without anyone complaining that they can’t delete books, then there seems little need to maintain the code that implements the feature. Chop, chop. Last, it seems that there is some confusion about new, edit, and new_and_edit methods on the Books controller. Looking at the code I see that new_and_edit is called by both new and edit, but is never accessed directly by the user’s browser. Therefore it should be made private (Axeman ignores private and protected controller methods). With all of these changes, the Books controller is quite a bit cleaner.
Also on the executioner’s block should be methods with low hit counts, that is, ones that appear at the very bottom of the lefthand column. This requires more knowledge of the user story for each page than does completely unaccessed pages. For example, you could have a page which displays some tax information which is only accessed once a year by one person in the organization. Therefore a low hit count should be expected, and the page should not face the axe. But most other kinds of pages should probably be removed if they haven’t been accessed frequently. The default time period is 3 months, which I think is about the timespan in which you’d expect something to be accessed at least a few dozen times. If it’s only got one or two clicks, chances are good this was just someone who hit the wrong link, or perhaps was just curious. Truly useful pages will have hundreds or thousands of views, depending on the size of your user base.
What about the idea that a page does offer useful features, but people don’t know about it? If you think that this is why it’s unused, then you need to find a better place to link it, or a better way to educate your users. The bottom line is that it doesn’t matter how theoretically useful a page is: if no one is using it, then it is not actually useful.
And keep in mind that this tool (and in fact, the entire concept of aggressive feature culling) is most effective not as a one-time event, but as a habit over time. A page which might have been extremely popular last year could fall into disuse when another page is added which provides similar but slightly improved functionality.
This plugin was the result of just an hour or two of hacking, but I’ve already been surprised at how useful it has been in my production apps. New ideas are suggesting themselves as I use it, including watching for unused partials, showing changes over time in a visual fashion, or even trying to look for unused model methods. For this last item, it’s been my experience that over time, model start to bristle with methods, many of which are remnants of historic functionality and no longer used, though this will be by far the hardest one to implement.
Another feature that I’ll try to add soon is a logfile anaylzer which scans production.log in a manner similar to how AWStats processes Apache’s access.log. This will allow the importation of historic data, and will also make Axeman more suitable for use on high-traffic, public-facing sites, where hitting an external SQLite database on each pageview may not be acceptable.
A subtle but powerful point that is driven home by the usefulness of this plugin is just how much design is an evolutionary process, not a one-time occurrence. Of course I know this, as do most of us, but I’m finding that Axeman makes it tangible. Here’s a piece of code which exists for no other reason than to help the application’s design change over time. The only other component I can think of that really acknowledges this is migrations, but these are more at the underlying technical level, rather than at the level of user-facing features.