CodeSOD: By Template

Kimberly L sends us an example of what is clearly template-generated code, so I think this is an opportunity to go on a rant.

Now, the important thing to note here is that this code was in a packaged library that they were considering using.

if ("development" !== 'production') { deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \'adjacency\'}', 'graph/sankey'); } option.emphasis.focus = 'adjacency';

Clearly, the condition there is always going to be false. But just as clearly, no human wrote this code- at least I hope not. Someplace on the server side, in some language or another, there's something like:

let env = "development"; let template = `if ("${env}" !== 'production') { … }`; println(template);

Now, that this got released as a library and isn't just some server-side rendered JavaScript is absolutely the big WTF. But generating JavaScript via templates, this way is a bigger WTF that we usually give it credit for. I'm annoyed by this, so let's talk about how to do templates-generated executable code well and how developers frequently mess it up.

One of the core things we need to think about in terms of designing software modules are the interfaces which connect them. This is true whether we're talking object oriented design or functional design. A code interface is like a user interface, and should specify the knobs and buttons we can push to accomplish things. Broadly speaking, we could think of our interface as the listing of expected inputs, expected outputs, and potential side effects.

Now, someplace in their templating engine, they might have that definition. "These variables exist for use by the template," may be defined on the server side. So people writing templates know they can use ${env} in those templates.

That much is fine, honestly. Okay, not fine, but we'll come back to why it's not fine in a moment. But assuming we're template-generating code, there are two problems with spamming variables around the template.

First, it makes the template potentially harder to read. The content of the template is now a mixture of two layers of our application: the server side variables and the client-side code we're generating. (This applies to any code generation system, not just code generation for client-side code, but that's the example we're looking at.)

The second is that the output artifact- the generated code- is frequently stuffed with nonsense like if ("development" !== 'production'). The artifact is not readable or easy to understand, because the connection to the inputs is not readable or easy to understand.

So, with that said, I want to suggest an easy fix to generating code via a template. This is a minimal effort thing that will mean we'll never see ugly, confusing template-generated code ever again. If you're using templates to generate code, use this one weird trick: variables.

let env = "development"; let template = ` //inputs let activeEnv = "${env}"; //body if (activeEnv !== 'production') {…} `; println(template);

This gives us an interface. We have a clear definition of what the inputs to this module are (populated via a template), and we have a clear picture of which part is the behavior. Plus, the output artifact is easy to understand- we understand that activeEnv is populated or generated elsewhere, and what the role of the variable is in the name, and all of that. It's significantly better.

If you're template-generating code, please do this. It's easy, it's cheap, and it makes the output more readable, but it also makes the template more readable.

And once you're looking at all these templates with clearly defined inputs and clearly defined outputs, you can move on to the next step: stop using templates to generate code. You now have a clear sense of what the interface to this code should be, so build an actual interface where you can call this code as a module, passing parameters as appropriate.

Because, honestly, executable code should not be generated by templates. Oh, yes, there are literal C++ templates, or macro systems, which technically do that, but these systems generally aren't operating on data as stringly typed. I'm talking about specifically template engines, a thing I see too damn much of.

Look, I understand, sometimes it's faster and easier to just populate a template with variables and then execute the code. I've done it myself. I've done it with OpenGL Shaders, even, which have a clear and well defined specification for how you should populate their variables from outside the shader program itself (spoilers: template generation is not the right tool).

But while it's a fine quick-and-dirty hack to get something done, I'm going to make the controversial statement that *any template expansion to generate code is a code-smell, and a sign that you've gone down a bad path and need to refactor this at some point.

In any case, this is my rant about template generated code. Sometimes they make a WTF. Sometimes I see just one too many examples and I have my own, extreme, WTF reaction.

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

This post originally appeared on The Daily WTF.

Leave a Reply

Your email address will not be published. Required fields are marked *