Three years ago I started writing a Rails plugin that could create customizable table views which reload via Ajax. The plugin was eventually used for the ICS Festivalmanager which is used to help organize the Wacken Open Air Festival since 2010.
Having re-written the plugin from ground up I want to share some bad design decisions I made while writing the first version of Flexirails.
1. Don’t couple your plugin to a persistence layer
Flexirails was supposed to represent parts of our domain objects, in a customizable way. At first I tightly coupled my presentation logic right to the persistence layer ActiveRecord because I added all kinds of search logic right into Flexirails.
It took me some time to realize that this was a bad design decision. Here are some things I added, along with some reasons why it had been a bad thing to do:
-
support for different persistence layers and different versions of the same persistence layer. E.g. supporting ActiveRecord and DataMapper, or different versions of ActiveRecord. Sometimes it’s better to leave these decision to the application developer so he can use whatever persistence layer he’s using, e.g. the file system, mongo db, whatever.
-
support for all search queries I could think of. E.g. supporting all comparison operators, similarity, MySQL null search etc. While this might seem good at first, it also means that you’ll need to implement all queries for a different persistence layer as well, even though it might not support all of them or you might only need a very small subset for your application.
-
support for combinational logic search queries. E.g. (x AND y) or (x OR y). Again this might seem good at first sight, but it’s hard to build a good generic UI for this. A problem domain specific search form will outperform your generic approach many many times in terms of user experience.
2. Don’t add to much UI customization options
In the original Flexirails version users could reorder columns, hide them, sort by attributes, select multiple rows for batch editing and more. Even worse, users could build specific views, save them under a specific name, manage these views, etc.
Again, this was a bad decision. Here’s why:
-
while customizable views might be great for some power users, this point get’s really irritating for the user as soon as she switches her PC or user account and can no longer access her own views. Dictating a single common User Interface is performs much better in those scenarios.
-
when adding new columns you need to have a clear upgrade path for old views, so they display the new informations as well. This can be very irritating for your users because you might show columns they never knew existed, or they might not even know that a new column was made available. Also you might end up destroying old views an user created, leading to the worst case scenario of any computer system: not worshiping the users data (and the time the spent creating it).
-
Adding too much functionality into a single view leads to an “one-size-fits-all” problem, where more and more features are packed into a single view. Having multiple views, each with a single, clear responsibility is much better and more easy learnable, understandable for a user - see the Unix philosophy.
3. Don’t monkey patch ActionController::Base
At first I did not think about how to properly apply Object Oriented design principals to Flexirails, leading to a custom DSL, which was monkey patched into ActionController::Base so I could configure Flexirails inside of my controllers.
A custom DSL is a big problem since new programmers need to learn it before they can use the DSL. Adding simple features can be problem as well, e.g. supporting different permissions; showing different content to different users.
This became somewhat of a problem since adding all this on a class level scope in your controller tends to get messy pretty fast. Also, DSLs tend to be unintuitive to be extended, so don’t do create your own DSL (unless you have a really good reason to do it) - and don’t ever monkey patch ActionController::Base.
Conclusion
Whatever you did three years ago, take your time to revisit your old work. You’ll be astonished by how much you’ll learn just by looking & improving on the code you wrote all those years ago ;)
Also, Flexirails 2 turned out really great - it’s completly decoupled from my persistence layer, so I am able to use it with what ever data source I like (currently filesystem, datamapper and activerecord 2.x and 3.x). Also, I’ve removed both searching and most UI customizations, leading to much cleaner and maintainable code. What I like most:
- I reduced the total number of code lines in Ruby from ~ 2000 to ~ 128, and code lines in JavaScript from ~ 1400 to ~ 400.
- It’s extremly easy & intuitive to extend and change - also due to the small code size
I might open source it sometime in the future. But that’s a separate blog post on its own.