Decoupling Features (so your support team sleeps easier at night)
You're working on a client's existing site, and they ask for some new functionality. Your first thoughts are: "Yay, some work!" Quickly following that you think: "What do they want? How am I going to build it?" The often unasked question, however, is "How can I integrate this new functionality with what they already have, without making maintenance a nightmare, or even worse, impacting existing functionality?"
This is a tough ask, as the new feature has the potential to reach across multiple pages, content types and styles.
We can build it!
For simpler features, it's easier to confidently introduce the new parts. For example, if your client wants a blog, you know that you can create a blog content type, give it an image field and a taxonomy reference field for tagging, and you're almost done. Build a couple of views displays and wrap the lot up in two features. One hook_update_N() function later and you have a scripted deployment to add your new blog articles, listing, tags and menu items in a single drush updb command. Easy, right?
But even with this simple example, what about theming?
The example assumes that there's no particular theming requirements for the blog articles and listing, and that the theme will handle all of the elements gracefully. But if changes are required, how do you prevent inadvertent CSS bleed from new style rules over into existing content types. Equally, if new styles are introduced, should they apply elsewhere on the site? Should, in fact, any new blog-related CSS live in the module layer, with the definitions of all the functionality, keeping all the related code together? Or should we dive into the theme and add styles that would become redundant if the blog were ever deprecated?
These questions deserve consideration, and I fear that they are often ignored, with a blasé "of course CSS goes in the theme!" attitude.
There is evil there that does not sleep.
Let's talk about Webform.
The Webform module is amazing. It is really powerful and allows site builders and site managers/editors to quickly craft complex forms for capturing and storing data, reporting on that data and even allows limited workflow functionality.
But for many business cases, the out of the box functionality simply is not enough. They want to be able to take payments and to sell things - tickets, time, etc. They want to be able to prepopulate fields with values and modify form elements based upon other field values.
Now we need to start really thinking.
We can modify form elements with hook_form_alter() - that's fine. But how do we limit it to webforms? OK, we can use the Form ID - it has the form 'webform_client_form_NID' so we can look for that. But each webform has a different numerical ID, so how can we limit our customizations to just a certain form, or a certain subset of forms? This is where we can get messy and we need to remember that this functionality is for life (well, the life of the project), not just for Christmas. However we do this, it needs to be maintainable.
Possible solutions.
If we want to identify a single specific webform, we can use its Node ID, which is an obvious choice. We can put that NID into a variable and into settings.php, which avoids hard-coding the NID into the functionality. The drawback of this method is that NIDs can change between environments, so your donation form might have a different NID on your development environment than it does on the live environment. This, at the very least, complicates deployments.
If we want to modify a subset of webforms, maybe we can use a content type. For example, Webform can be attached to any content type, so we can easily create a 'donation form' content type and make our modifications to any webform of type 'donation form'. In this way we can manage separate functional modifications to other forms, e.g. event registration or lead generation. This too works, up to the point where the site owner wants to combine both your donation/payment functionality and your event registration functionality into one form. How does one rationalise and implement such a request? And how do you then limit this combined functionality to specific forms? Are we back to square one with node IDs set in settings.php, and its attendant maintenance nightmare?
There has to be a better way.
I quite like to use fields to bring configurability to new functionality. A couple of fields in a vertical tab field group lends a nice air of a proper settings form to your node edit page. Once you interrogate your node for the values of these settings fields, you can then control your custom functionality at a per node level, which is a really neat thing to be able to do from both a site admin and site maintenance point of view.
The problem here is that you've two separate items that are tightly coupled: your nifty new module/code and field configuration updates to any content type that you want to make use of it. You then need to bear in mind that you need to maintain the Features for any such content type. It would probably be worth bringing this logic to its natural next step, creating a Feature for all the Field Bases of your new fields, guaranteeing that they exist and are consistent with the machine names expected by your custom code (and more easily allowing field bases to be shared across content types/features without conflicts).
Somewhere, over the rainbow
Here's my version of the Holy Grail: imagine a system where you could indicate on a settings page, which custom functionality should apply to which content types, and settings controls would then magically appear on the relevant node edit forms... As of this writing I have not seen a working model of such a system, but I can dream.
Maintenance of your site should never be far from your thoughts when introducing new features/functionality. As Support Lead for Annertech, I have to deal with a lot of legacy code, and therefore think about these things a lot. If you'd like to chat about support for your Drupal website, or ongoing development in a sustainable, maintainable way, give us a shout.
Anthony Lindsay Director of Managed Services
With decades of experience, Anthony leads the Annertech Managed Services Team, delivering top quality design, development, and, ultimately peace-of-mind services to all of Annertech's wonderful clients.