Friday, December 3, 2010

Stupid Template Languages

For years I've been absolutely certain that I really prefer stupid template languages any time I'm generating HTML. The less the template language can do the better. Since I spend most of my time coding in Python you might assume this applies just to Python, but I think it also applies to anything where you have the power to readily mix HTML generation and code.

The biggest annoyance I have with smart template languages (Mako, Genshi, Jinja2, PHP, PerlColdFusion, etc) is that you have the capability to mix core business logic with your end views, hence violating the rules of Model-View-Controller architecture. While the web can be hard to match to MVC, in general you aren't supposed to do that sort of thing. I've made the mistake of putting core logic in the wrong places in the past, but I'm proud to say I've gotten good at avoiding that particular mistake.

I don't work in a vacuum.

I often work on projects crafted by others, some who decided for arcane/brilliant/idiotic reasons to mix the kernel of their applications in template function/macros. This is only possible in Smart Template Languages! If they were using a Stupid Template Language they would have been forced put their kernel code in a Python file where it applies, not in a template that was supposed to just render HTML or XML or plain text.

What it comes down to is that Smart Template Languages designers assume that developers are smart enough to avoid making this mistake. Stupid Template Languages designers assume that developers generally lack the discipline to avoid creating horrific atrocities that because of unnecessary complexity have a bus factor of 1.

So what is a Smart Template Language?

In my own vernacular, template languages that let you write functions/macros are what I call a Smart Template Language. Some of them are brilliantly executed, the example of Jinja2 comes to mind, but invariably I suffer through abuse of its Macro control structure as implemented by others.

Misery Cubed a.k.a. Genius Template Languages

Next comes Genius Template Languages, which take things a step further. These template languages allow you to not only define functions/macros, but also let you embed unrestricted Python (or Java or Ruby or whatever) in the template. This 'feature' lets you code your entire application in the templates!  In the Python world what comes to mind is Mako and Genshi, but I'm sure there are many other tools with this 'capability'.

I like Stupid Template Languages!

Stupid Template Languages don't let you define functions/macros. They don't let you embed Python code. They barely let you define variables and often have simplistic control architectures.

For Django efforts, which is about 70% of my work, I like the Django Template Language (DTL). Since it is used by a huge community, there are a ton of useful apps which have it as a dependency. Switching away from it would mean cutting myself off from a large ecosphere of tools I can use to not reinvent the wheel.

Back in my Zope/Plone days I really, really enjoyed the Template Attribute Language (TAL) because it was stupid too. If I needed an XML generation template language and could import it easily I might consider using it again, or perhaps Chameleon, which is a new, improved version . The downside is that they come paired with another tool paired with it, METAL, which gave it macros. My own experience with METAL is that it was all too easy to do what we developers do with Smart Template Languages.

But DTL and TAL are slow!

So what?

If you want to boost your performance, first try caching. There are a ton of tools you can use, with Varnish being one I keep seeing in action. Read the docs on your favorite web framework's caching engine and apply what you learn. And Djangonauts should read up on Mike Malone as much as possible.

If after all that the site still delivers slow content and it appears to be a template language issue, then identify the bottleneck content and consider alternatives for that one portion. My favorite response is a bit of AJAX. Use your framework to render the content as JSON and have JavaScript parse it into legible content, a task which JQuery makes trivial.

20 comments:

Hanno said...

TAL in its Chameleon implementation isn't slow at all. In terms of raw performance it's in the same league as Mako or Jinja2 - depending on the exact benchmark sometimes a bit faster and sometimes a bit slower, but essentially they all use the same basic approach.

That said, choosing a template language isn't about performance anymore these days, as all the modern implementations compare equal on that level. So it's really your audience (developers vs. integrators / designers), the ease of use and other usability criteria which are key.

pyDanny said...

Hanno,

Thanks for the clarification. Chameleon looks awesome, and if I ever used it I would write something that would check for xmlns:metal="http://xml.zope.org/namespaces/metal" in the templates and raise an error if it found it. :)

Anonymous said...

print 'my preferred templating mechanism is just %(template)s - simple but effective'%{'template':'string+dict'}

pakalk said...

Look at Twig and PHPTAL (for PHP templates)

Petri Savolainen said...

It could be argued that METAL is not really a macro language but a markup inclusion language with a somewhat misleading name. Hmm maybe it could be renamed to MIL? ;-P

Despite having used TAL & METAL for many years, it never occurred to me to (ab)use METAL as means to put application logic into templates.

Given their relatively verbose syntax, I have a feeling the syntax overhead of TAL & METAL might actually work as a deterrent against such abuse. Curious thought, that..

But, since you are familiar with METAL and consider it too easy to abuse, perhaps there's something I did not understand. Care to elaborate a bit more?

Lennart Regebro said...

I like templess, which isn't just stupid, it's positively moronic. :) But never get to use it. :-(

http://pypi.python.org/pypi/templess/0.3

huxley said...

In my opinion, just the graph and the first two paragraphs of Code Irony's "Fastest templating engine ever. Period." are the best things ever been written about template speed:

Fastest templating engine ever. Period.

pyDanny said...

Petri - You are correct, METAL indeed does not let you put business logic in your templates. But it lets you hide critical parts of your views in obscure places. How do I know? I did it myself!

pyDanny said...

huxley - Brilliant! http://www.codeirony.com/?p=9

mike bayer said...
This comment has been removed by the author.
brad clements said...

Hmm, I don't recall seeing any logic features in metal. I don't quite see how business logic can be implemented using metal because it doesn't have any control flow terms. (vs. tal:condition)

anyway I have a TAL implementation in xsl that also does METAL. I love it, I use it client side in the browser or server side with libxslt or lxml.

I also have a javascript version of just tal, client-side.. works well enough for my needs.

cats2ndlife said...

You can turn off Python code blocks in Genshi's configurations. You can also do that in ASP and JSPs too. If you don't want your team to put code blocks in templates, just turn it off. I always do that with Genshi and JSPs. While I agree with you on most of the points, I don't see having code blocks support in the template language is such a big deal. There are many instances using code blocks is clearer/easier to reason with. The only reason I turn off code blocks is because I don't want to mess up my markup IDE.

Eric said...

The way we use templating engines are really counter to the way that HTTP and hypertext was designed.

There's an aspect of hypertext that we rarely talk about, but use blindlessly when we put an image on a page. It's the idea of transclusions. We use it all the time to insert an image on a page, but we rarely use it to include text on a page.

I have found with some preliminary testing that by using RESTful resources, which can be cached edge side and then transcluding the resources client side with Ajax or "iframe seamless" performs extremely well.


You can see a preliminarily example of doing client-side
transclusion at this URI:

http://dl.dropbox.com/u/1890692/hyperblog/index.html

That URI presents two resources, an index page and an entry page with all the redundant site elements into the HTML pulled in using current transclusion techniques.

If you view the source code on that page, you can see how the header and footers, because they are site elements and not resource elements, they're transcluded client side, just like any other static content. They can utilize the benefits of edge-side and client-side caching because they rarely change. This makes the only thing actually in the response, the actual resource being requested, which can be cached independently of the header and footer.

I am not sure how constructive or destructive to SEO this is. I'd like to think that since only the relative resources are in the HTML, that it would help SEO because the HTML is a highly concentrated view of the content instead of a diluted version with dozens of unrelated links in the header and navigation.

jalpino said...

I thought flexibility in a language was a good thing? Templating with the ability to embed business logic isn't the fault of the language as much as it is the developer. What is to stop someone from embedding business logic in their controllers vs say a service layer?

pyDanny said...

@cats2ndlife - Turning off Python code blocks in Genshi's configuration sounds useful, but my contention is that it shouldn't have the code blocks there in the first place.

pyDanny said...

@jalpino - I think a language or tool should be flexible, but a few hard or soft rules mean the difference between a big plate of spaghetti code and a maintainable project.

For example, a lot of people don't like Python's use of whitespace to delineate code blocks. Yet I consider that a feature and probably 95% of the Python community agree with me. Should we add a setting to Python that lets you have the flexibility to turn that feature off or on as your developer feels it suits their needs?

temoto said...

Don't you feel like Stupid Template Languages must be far faster than Smart ones? And the opposite sounds like a huge fail in, DTL, in particular?

pyDanny said...

@temoto - Are you saying that stupid must mean fast and DTL is slow? And that is a big failure of DTL?

The speed of your template language is generally a small issue when it comes to performance problems and is normally the last thing you need to worry about. Which is why this article is so amusing! http://www.codeirony.com/?p=9

In a nutshell, before you care about template performance, take a look at business logic and database access speeds.

Roman said...

Hi, You can also look at C# incarnation of TAL in project SharpTAL: http://sharptal.codeplex.com (which was ported from python implementation - project SimpleTAL). I added sme interesting extension to METAL such as macro paameters and macro imports. Some ideas could be implemenmted in my favourite python template engine Chameleon :)

Regards
-Roman

Jonas B. said...

Not all logic is business logic. There is template logic too. Take the simple example, if user is logged in put personalized greeting here, or the color scheme of user's choice.

Template logic gets more complicated from there the more complex your app is. You need a full language to express these things, otherwise you just move things from your views to your controllers. That's just as bad as the other way around!

Too stupid template languages forces you to do that.