This is the third post in a series on writing cleaner code in your WCMS Velocity formats.
Like templates in XSLT, macros are a useful and idiomatic way to help separate the parts of our Velocity code so that we can better read and re-use them.
There’s an important difference between XSLT templates and Velocity macros, though: while XSLT templates only accept simple scalar values via the
<xsl:with-param> element (unless you want to load up EXSLT), Velocity macros readily accept complex types as parameters, such as nodes or a
Because Velocity macros can accept complex types as parameters, they’re a little more flexible than their XSLT counterparts and in some cases allow us to write fewer formats. However, this power comes at a cost and should be used with care.
Changes upstream can break formats, and it’s easy to miss
When a complex type like a node is passed to a macro, we couple the code in the macro to the structure of the complex type through the use of methods like
When (not if) we change the structure of the complex type (adding new node elements, renaming elements, and so on), any macros that get passed data of that type must change as well. Aside from painfully-vague runtime errors, Velocity in the WCMS doesn’t give us much help when it comes to figuring out what we’ve broken.
Contrast all this with a Velocity macro that only accepts simple values, such as
While Velocity runs on Java under the hood and can poke at some of its tools, Velocity itself isn’t a full-on programming language — it’s a templating language. In object-oriented languages like Java, we can use things like abstraction, interfaces, and type declarations to help deal with some of the challenges inherent to complex types.
Complex parameters force you to read more code
As I mentioned in the previous posts in this series, clean, easy-to-maintain code is about readability, and a big part of readable code is the ability to look at one specific piece and understand what’s going on without having to step through a bunch of other parts of the code.
If our macro accepts a parameter like
$profile, we have no way of knowing for certain the structure of that data just by looking at the macro. If we wanted to make any changes to the macro or just understand what it was doing, we’d have to follow the call chain backwards until we found where the parameters were defined — or worse, pull up the data definition.
Contrast this with a Velocity macro that only accepts simple values, such as
$declaredMajor. There’s no hidden structure to work out, so when it’s time to make a change to the macro, you don’t have to go through the whole process of re-acquainting yourself with the XML structure of your framework’s content chunks.
Putting simple values into practice
Like the other posts in this series, the bits of advice presented are guidelines and recommendations, not absolutes. On the back of writing a number of frameworks in the WCMS, I can tell you that building one using only macros that take simple values is super unlikely. Instead of thinking “Simple values good, boo complex types,” make a habit of asking yourself “Is there a way I can write these macros in a way that uses simple values instead? What are the trade-offs for each approach in my situation?”
In some cases, you might find that you can’t get away with using only simple values. In the cases that you can opt for simple values, though, your teammates (and your future self) will be really happy you did.