What are “semantics”?
Semantics are deeply ingrained with most of what we interact with. Traditionally, the term “semantics” refers to the meanings of and relationships between words. In a broader context, I prefer to think that semantics are implications from design.
Everything we use in our daily lives, including words, have been designed in one way or another. The design of these daily “things”, guide us to use them in a specific way.
Apparently, the design of a bowler hat implies butt apparel.
One of my favorite examples of good design for semantics is that of a door. Many doors can be pushed from one side and pulled from the other. Given appropriate signs, it should be obvious which side expects what behavior. Unfortunately, despite clear signage, it is still possible for users to try to pull or push on the wrong side!
A well-designed door, however, should not even need to help of signs to imply its usage. By removing the handle on the “push side”, we leave the user no choice but to push the door to open it. Similarly, by adding a handle on the “pull side”, we give the user the ability to pull the door to open it.
How does this apply to software development?
It is easy to see why designing with semantics in mind is important. With software development, we can apply a similar mindset to produce intuitive APIs.
A helpful way to approach software design is to first think of how the user will interact with your software. Aside from planning, drawing diagrams, and other useful planning activities, a valuable exercise can be to write a simple script/use case simulating the use of your final product.
An even more productive exercise would be to write the use case as an automated test. By no means am I suggesting Test Driven Development is the most effective way to work, but it helps force oneself into thinking carefully about the function calls, class instantiations, etc. a user would have to make to achieve their goal using your API. If the steps you write in the test are already cumbersome, then you have good reason to believe the user will feel the same too!
Good design is important from the highest to the most fundamental levels. Take the Python language itself as an example. The semantics of the language make it quite human readable.
with open("/path/to/foo", "r") as file: # Do something.
In this snippet, we use a Context Manager to safely open a file to read.
with implies we are performing an action with something, making the code
incredibly natural to read.
Ultimately, there is an infinite number of perspectives we can take when designing for semantics. Taking a page from The Zen of Python:
There should be one– and preferably only one –obvious way to do it.
In other words, make your APIs obvious, a user should not have to choose between five different ways of doing the same thing. Just give them the one, simple, way!