Today’s developers are cursed blessed with a massive list of libraries and technologies for solving problems. And some of the worst sins against code stem from simply selecting the wrong tool for the job. It’s recently become clear to me that when considering tools, the risk for picking the wrong tool for the job is strongest near the boundaries. Consider some common technologies for building web applications today. Each technology has a specific purpose.
- HTML provides semantic markup of our content
- CSS separates our styling from our markup
- JavaScript provides behavior
- Server-side languages manage the business logic
- SQL is useful for RDBMS data access and manipulation
And here’s the thing: It’s the boundaries and interactions between technologies that get people in trouble. Let’s discuss the interactions between each of these technologies and common situations where developers select the wrong tool for the job.
HTML and JavaScript Boundary
Avoid putting HTML in JavaScript strings for “poor man’s templating” like this.
Note how the HTML for generating a row is buried within the for loop and set to a variable called carRows
. It’s tempting to put HTML in a JavaScript string so that you can dynamically inject HTML onto the page. But markup should ideally be native HTML. When selecting a templating solution, look for one the utilizes plain old HTML for the template source. Both KnockoutJS and AngularJS templates use this approach. If you’re storing HTML in .js files, think twice – JavaScript is typically the wrong tool for storing markup since you lose the benefits of staying native (code coloring, syntax checking, etc).
On the other side of the coin, avoid placing complex JavaScript on DOM elements in HTML:
[pageview url=”http://jsfiddle.net/housecor/3cXwh/embedded/html” height=”100″]
It’s tempting to put JavaScript directly in your HTML. But this mixes concerns by injecting behavior directly into your markup and eliminates the opportunity for caching and reuse. The unobtrusive JavaScript movement really helped diminish this problem, but new frameworks like Knockout and Angular have started pushing JavaScript back into HTML markup. There’s some clear benefits to simple declarative markup in HTML since it’s more discoverable and readable when the bindings are simple. But be careful, when much beyond simple declarative bindings is involved, JavaScript belongs in .JS files and HTML belongs in .html files.
HTML and SQL Boundary
Here’s two simple examples of ignoring the important boundary between data and HTML:
SELECT '<strong>' + Username + '</strong>' FROM Users
Relational databases should contain raw normalized data that can be leveraged for a variety of purposes. Thus, storing HTML in the DB or injecting HTML in query results as shown above is problematic. It binds the presentation and data storage together making reuse difficult.
One common exception is content management systems where users must be able to store and manage HTML which is persisted in the database. This scenario sacrifices data reuse and normalization for end user management power. Think very carefully about saddling data with presentation concerns since it radically reduces flexibility by “hard coding” in the presentation. Mixing presentation and data should be avoided. They’re separate concerns and should be treated as such.
HTML and CSS Boundary
It’s a very similar story with the HTML/CSS interaction. Avoid doing this:
[pageview url=”http://jsfiddle.net/housecor/6gT8f/embedded/html” height=”75″]
It’s tempting to place styles inline in HTML, but this mixes semantic markup with style. These are separate concerns and doing so eliminates the opportunity for caching and reuse of CSS and hinders maintenance down the road. The DRY principle applies here: If you’re going to use a style in more than one spot, it pays to declare the style once in a separate stylesheet.
SQL and Server-side Language Boundary
It’s easy to create highly dynamic SQL in a server-side language like C# using strings to generate SQL statements, but stored procedures and ORMs often provide a safer and more elegant approach to generating dynamic SQL. Writing dynamic SQL in strings opens the door to SQL injection vulnerabilities and eliminates the opportunity for DBAs to manage and enhance the performance of such queries. SQL in strings also introduce a high risk for run-time errors in the application when schema changes occur. ORMs provide a strongly typed interface to the database and will thus fail to compile when the schema becomes out of sync with existing database queries.
JavaScript / Server-side Language Boundary
Writing dynamic JavaScript in a string on the server leads to code like this.
string googleAnalyticsScript = @"var _gaq = _gaq || []; _gaq.push(['_setAccount', '" + googleAnalyticsKey + @"']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();";
This is the most common tool misuse I see: using a server-side language to generate dynamic JavaScript. In the vast majority of use cases JavaScript should remain in a .JS file. JSON can be returned from the server to provide dynamism. I won’t go into a specific example here, but check out this blog post on the JavaScript configuration object pattern if you’re curious how to pull off highly dynamic JavaScript without resorting to generating it via strings on the server.
So before you dive into writing that next line of code, think twice – are you near a boundary? Does a more elegant solution exist on the other side of the boundary? Clean coders should strive to stay native: – the file extension should be a good indicator of the technology that lies within.
Stay Native
Of course, the importance of staying native isn’t at all specific to JavaScript. There are a variety of opportunities to find yourself struggling with strings when there’s a less painful alternative that involves staying native. The bottom line is, avoid using one language to write another language or format via strings. Creation of these formats dynamically is a solved problem in most popular languages these days, so be sure to leverage libraries to generate such formats. A good rule of thumb is to strive for one language per file. Generating any of these items via strings requires the reader to understand the interactions of two technologies simultaneously. And doing so often breaks tooling support as well. So if you notice you have two languages in one file, think twice about how one could be eliminated by solving the problem in a different, and more elegant way.
If you’re interested in learning more about writing clean code, check out my new Pluralsight course: Clean Code: Writing Code for Humans
See other boundaries to consider? Chime in below or join the discussion on Hacker News.
`SELECT ‘‘ + Username + ‘‘ FROM Users` is likely to fail, because that query is looking for a field named `Cory` on the `Users` table. Instead, the example should be something more along the lines of “SELECT `Name` FROM `Users` WHERE `Name` = ‘” + Username + “‘;” where you are extracting the content shown in the picture that followed.
Derp, HTML ate the comment, but I trust you get the idea…
Hi Andy – In the example query, Username is the column name, so the example query will return each username in the table, surrounded by the strong html tag. Sorry if that was unclear.
Hi Andy/Cory,
I just wanted to join the conversation and highlight the importance of the quote that Andy used in his query.
Assuming you have a field called group in your table, then the following query will not work:
Select group FROM tablename;
since group is a reserved word. The following will work, however:
SELECT `group` FROM `tablename`;
I just mentioned this because this is a very common issue.
Indeed, the SQL command is wrong and not respect the valid syntax :
SELECT ‘‘ + Username + ‘‘ FROM Users
Below the valid syntaxt:
SELECT name FROM Users Where name = ‘Cory‘;
I’m sorry, I think my post is unclear in this spot. The two images under the SQL example aren’t related. One is showing HTML in a db. And the other is showing injecting html into a SQL query as a string. The latter works in SQL server as posted and I never meant to suggest that the SQL query is querying the table posted in the other shot. That said, thanks for the feedback!
What’s your opinion on html templating engines, such as Handlebars and Razor? Does this make more clean reusable code or just violate the boundaries?
Great question. That’s all about perspective. Proponents of the unobtrusive JavaScript movement would generally not like the approach, simply because it does tend to inject some light logic into the markup. However, the unobtrusive JavaScript movement has gone out of vogue in favor of two-way binding using popular libraries and frameworks like Knockout, AngularJS, etc. I have no personal qualms with Handlebars or Razor, though I admittedly haven’t used the former in prod. I will say the truly exciting tech that will solve these problems more elegantly is web components and the Shadow DOM. We can enjoy the future today in Polymer or via directives in Angular. Knockout is getting something similar to web components in v3.2. Exciting times!