We all recognize that we should be writing semantic code. Maybe, you're even using
<section> or
<em> correctly, and feel pretty good about yourself. But, are you also considering the implied contract that exists when you code?
Let's imagine that a customer requests a text link, “See More,” which should reveal additional text on the page.
<a href="#">see more</a>!
There are, however, much more practical reasons to care about semantics: contracts.
Usage Contracts
Any time that you use some functionality provided by browser vendors, your programming language, or an API, you are relying on a contract.
On one side of the contract exists a provider of functionality. For example, when you use an
.
Browser functionality contracts
Let's return to the “See More” example from earlier. In the early days of JavaScript, developers would write their JavaScript using a pseudo protocol,
javascript:
<a href="javascript:ShowMore()">See More</a>
Relatively quickly, they began to realize that this approach has at least one practical flaw: the JavaScript code is displayed in the status bar of the browser, which doesn't look professional. The answer to this dilemma was to move the code to an
onclick handler, and replace the
href attribute with an empty hash identifier, like so:
<a href="#" onclick="ShowMore();return false;">See More</a>
Of course, this didn't solve everything. If
ShowMore has an error,
return false is never called, and the page is scrolled to the top - certainly not desired behavior.
Today, developers separate their HTML and JavaScript and use proper event prevention techniques:
<a href="#" class="show-more">See More</a>
$('a.show-more').on('click', function(event) { event.preventDefault(); ShowMore(); });
But guess what? There are still plenty of problems with this approach: opening in a new tab, bookmarking or copying the link does not work as the user might expect (especially, if there is a
<base> tag on the page), which results in confusion, and possibly, a lost customer.
Notice how all the problems described so far arose from a single fact. Developers have been violating their part of the contract with the browser makers:
href attribute must contain a correct URL, but, instead, developers were inserting all kinds of garbage.
If we try to conform to our contract, we must ask ourselves: "Why does the contract not allow us to do what we need?" The answer is simple: we misuse the
<a> tag. We only use it, because we want the text “See More” to appear and behave like a link. However, when we use words, such as "look" and "appear," is this not the domain of CSS? Isn't this "See More" link, in reality, just a button? This is easy to implement:
<button type="button" class="link-style show-more">See More</button>
$('button.show-more').on('click', ShowMore);
a, .link-style { color: @your-shade-of-blue; text-decoration: underline; } .link-style { font: inherit; background: none; border: none; cursor: pointer; }
Regardless of how many new ways to interact with links are added to future iterations of the browsers, this approach will continue to work, because we are not violating any contract with the browser. We are using elements to convey their meaning, and, instead of constantly working around new problems, we know that the browser will treat this correctly, as it understands what we want.
Sure, it might require a few extra lines of CSS to reset some of the default button styling. It's worth it! Don't be lazy.
Specifications as contracts
...those who did know the semantics, and knowingly ignored them.
In 2005, a large portion of the web development community learned the hard way that ignoring HTTP requirements is bad. The newly released Google Web Accelerator prefetches URLs on a page to minimize the user’s waiting time on a click.
This wrecked havoc in applications, which ignored HTTP, and put destructive operations behind simple links. And there were many such applications.
The problem did not rest in the hands of the developers who didn't understand the semantics of HTTP methods. No, the problem came from those who did know the semantics, knowingly ignored them, and did not share the knowledge.
Maintenance Contracts
Confusing code is bad!
Contracts also exist between developers. Every time you write a line of code, you make a promise to future developers that it does exactly what it appears to do. In other words, confusing code is bad!
For example, using a filtering function (such as
filter in Python) to loop over items is incorrect, because there is an implied contract that filtering functions only modify the list, without having any side effects:
def show(item): print(item) fruits = ['orange', 'apple', 'lemon'] filter(show, fruits) # Incorrect usage of filter
Instead, use explicit looping over a list:
def show(item): print(item) fruits = ['orange', 'apple', 'lemon'] for fruit in fruits: show(fruit)
On the other hand, if you can rely on a contract, it, as a result, allows you to clarify your code. For instance, using
array_filter instead of a
for loop in PHP to filter items allows other developers to understand what is happening after a single glance:
$incomes = [20, 15, -7, 19]; $profits = array_filter($incomes, function($i){ return $i > 0; });
Compare the snippet above to using a
for loop:
$incomes = [20, 15, -7, 19]; $profits = []; foreach ($incomes as $i) { if ($i > 0) { $profits[] = $i; } }
The second version is worse, not only because it is longer, but also because
array_filter provides an expectation. It is clear that
$profits is a subset of
$incomes. There is no such convention with a generic
for loop, which explains why we can't make any assumptions, without first deciphering the internals of the loop.
Writing Properly Semantic Code
How do you distinguish between code that is and isn't semantic? How do you notice these contracts? For the most part, simply asking yourself questions should suffice.
HTML Tags
“I am using an
<a>tag and I need to put something in an
hrefattribute.
hrefstores the link target destination. So where does my
<a>tag link to?” Nowhere? Well, I can then conclude that I am probably misusing this tag.
Named Functions
“I need to print out the contents of my list in a
filterfunction. Is printing out items part of the filtering process?” Not really. That means I am misusing
filter.
Functions That Do Too Much
“I am writing a
getFoomethod, and need to add some modification code within it. Does modifying state make sense as part of getting foo? Would anyone who simply wants to get foo expect that something else happens?” Likely not!
Separation of Concerns
“I want to add some JavaScript within an HTML
onclickattribute. Is this correct?” The file you are modifying is
.html, right? Instead, place your JavaScript and CSS within
.jsand
.cssfiles, respectively. Always.
Of course, in many cases, some additional knowledge might be required to properly assess the situation. What matters most is to begin thinking about these things, and to avoid the principle “it (kinda) works, so it is fine enough.” Instead, always ask yourself, "What does the code I am writing actually mean?"
Conclusion
Violating a contract ensures that cooperation will break.
Cooperation always depends on effort from both parties. Most of software development reduces to cooperation: cooperation between hardware manufacturers, system programmers, browser developers, library authors, website developers, and many others. Violating a contract ensures that cooperation will break.
Code semantics is one such contract. Care about it for your own good!
Footnotes
Semantic: The term “semantic” is often misunderstood. In the context of this article, I use the term to distinguish the meaning of the code from the results the code produces. For example, semantic meaning of an
<a> tag is representation of a link, while the simple, practical meaning of it is clickable, underlined text.
Strictly speaking, an empty fragment identifier
# might be considered valid in this context. This technicality, however, misses the point I make in the article.
