Continuing on from part I, this week focuses on easily avoidable mistakes. It teaches techniques like being mindful of interfaces between programming languages, matching of closing entities, and being aware of the availability of functionality in alternative contexts and platforms.
August 02, 2020
No notes available for this episode.
Transcribed by Rugo Obi
Tip 5: Do All Your Opening Entities Have Matching Closing Entities?
Do all your opening entities, for example, tags like this
divs, opening parentheses, opening braces, etc. have corresponding closing entities?
Now, for example, if I go to the closing
table tag, and I delete that. Let's have a look at what happens to the layout here. I'm refreshing.
You can see the layout is completely broken, all those products are no longer visible.
I would have caught this bug if I were rigorous enough to check that the opening
table entity up there in line 27 had a closing entity. And here it does not.
Another way to detect this is to format the code.
And you can see here that the final statement here is indented by two spaces, whereas you'd expect it to be all the way across horizontally. This is indicative of a missing closing entity.
Let me give you another example of where rigor is called for with closing entities: parentheses as it relates to precedence in this case.
So here I have a typical product page. I submit that to Google via a sitemap. This is the code for generating the sitemap. I loop through the
IndividualProducts I have in each
store I serve, and then I use this custom function,
add to add to the sitemap.
And here I calculate the path for that particular product, and I pass in to the sitemap, the
lastmod: attribute, and I set that to be the
updated_at of a particular product, to tell Google that this particular page was last modified whenever that product was last modified. This is in order to save crawl budget.
So let's take a look at the sitemap generated by this. It actually has a bug. There's no entry in the
sitemap.xml for the
In fact, the last part is added as an attribute to the particular URL, even though that has no meaning within my website.
So what's gone wrong?
If we look over here, there's actually two functions. First there’s this
add function. You don't have to have parentheses in Ruby for functions, but I'll add them here to be clearer.
So we have this
add function and then inside that
add function we have the
revision_notes_path function and this accepts
add(revsion_notes_path(product: product, lastmod: product.updated_at))
lastmod: doesn't belong here, the
lastmod belongs with the
So what I really should have done is, add a closing parenthesis here, and removed the closing parenthesis here.
add(revsion_notes_path(product: product), lastmod: product.updated_at)
Now, if I run that code again, I will get the correct sitemap and I've run this earlier and you can have a look at it.
The actual URL doesn't have a
lastmod attribute, and the
lastmod attribute instead exists within the sitemap as it should.
Tip 6: Are The Constants And Entities Referred To Defined In The Current Context?
a). Are the constants (or other entities) referred to defined in this file?
What do I mean by this?
Let me open up this
translations.js file I tend to use.
So you can see here that it's kind of a self-contained translation unit.
I have these English and German translations for various terms, a function to switch the
locale, a function to translate a given
key and find out what the translation is, and so on. This sets it to a
So originally I had this in my React Native app, but then I wanted to include it in my React web app.
So I tested it pretty thoroughly in the React Native one, then I copy pasted it into the React web one, and then committed it and sent it to a coworker.
Of course, it did not work in that context. Why? Because I referred here to the
i18n module, which is imported here. However, this was not imported within the other project, so I made an assumption about the context which was untrue.
b). Are the constants (or other entities) referred to defined in this environment?
By environment I mean things like development, staging, production, tests, and so on.
Let me give an example of where this went wrong for me recently.
In PHP Laravel, they have these things called
AppServiceProvider, this gets called - rather the
register function - gets called when the app gets booted, something like that, I’m not so familiar with the internals, it doesn’t matter.
And what I wanted to do here was define a macro for the
Browser entity which I use in acceptance testing. This comes via the Laravel dusk package.
And this macro allows me to fill in hidden fields on the screen. This worked perfectly in the test env, however when I tried to run this on production or in staging, the whole thing failed.
Why was that? Well if you look here at the
composer.json file (this is kind of the equivalent of the
package.json file - It's where you include libraries for PHP) and this
laravel/dusk package where
Browser’s defined is only available in
require-dev, instead of the normal dependencies.
Therefore this constant over here,
Browser is only available in dev and testing, whenever those dev dependencies are loaded.
Therefore, this will fail in the typical case. The way around this is to wrap the whole thing in an
Basically something like “if the environment is test, then you can define that macro”.
c). Are the constants, or other entities referred to defined in this namespace?
So I'm going to pop over to the
SendInvitations.php file over here.
The first important thing to note is that this is contained within the
app/jobs namespace. It is not top level.
SendInvitation job when it gets called gets called via this
The first thing it does is check if the event passed to it has
invitationSent(). If so, it throws an exception, otherwise it alerts via email and SMS.
This code had a bug in it. The issue is that, even though PHP has an
Exception, it is not present within the
app/jobs namespace, therefore it failed, with the exception not being found.
What I had to do is say that
Exception was contained in the top level namespace like so, in order for it to work. I.e.
Be aware of what constants are contained within your current namespace.
d). Are the constants (or other entities) referred to defined in this platform?
Okay, for example, in my production website, I have this feature on my downloads page that whenever someone downloads a particular file, this thing comes up saying that the download was accessed.
This is part of fraud control, and you can see the corresponding code right over here, this
The important part here is that I use the
When I first wrote this code I assumed
fetch was available on all browsers. However, that is completely untrue.
Let me demonstrate that by opening up the, “Can I use” website for
Here you can see that it's not supported on Internet Explorer, it's not supported on edge 12/13, not supported on much of Firefox, not supported at all on Opera mini.
Therefore a certain percentage of my users, in fact you can see here, roughly 94% of my users are able to use
fetch, 6% aren’t, therefore I had to polyfill this.
An important thing for you to check is whether the features you're using, such as
fetch here, are actually available on the platform you're targeting.
This is also true, for example when you use special features on MySQL.
I once programmed an application with a bunch of features, assuming that the latest MySQL will be running on production.
However, there was a limitation there and it could only run 5.7, despite the fact that I programmed in 8, I believe. Therefore we had to get rid of a lot of features. So, I should have checked beforehand if I was being more rigorous, “what does my platform support” before writing any code.
Tip 7: At Language Boundaries, Consider Data Format And Naming Convention Changes.
Point seven, at the boundaries between two languages or systems, are you translating to the correct data format and naming conventions expected on the other side?
Interfaces between systems have a tendency towards breakage that -no pun intended- borders on maniacal. Thus, extra caution is always warranted here.
A common -air quotes- “dumb mistake”, is forgetting to translate data into a format that can be understood by the other side, and accurately preserves meaning.
I'll present a couple of examples of these.
My first example will be interfacing with the command line, a Unix command line.
Many programs call out to other binaries that are available on the command line. So for demonstrative purposes, let me show you a file I have here.
Law and Order.txt, I believe. Let's have a look inside, and you can see the contents of it there.
Now, let's try to work with that file from within Ruby. I'm opening up a
pry instance that's basically a repl here.
Now let's assign that file to a variable, we'll call it
file. And now I'm going to use the
system command to call out to the underlying shell. I'm going to pass it in the
cat binary, and then that particular file using string interpolation.
And it fails. You know that because it returns
Let's look at the particular failure.
cat attempts to open
Law, and there's
No such file or directory. Then a file called
and, then a file called
order.txt. i.e., it's splitting this entire filename into three separate file names or three separate shell words.
The general solution is to be mindful of this interface between Ruby and the underlying shell.
Most programming languages have utilities for passing arguments to the shell. One that's available in Ruby is this thing called
So if I call that here on the file argument, everything works as expected.
What this does is wrap up the overall file name in a shell word — a single instance, so to speak — and that makes everything work as expected.
You could of course do something simpler here, like wrap the whole thing in quotes, as I'm doing here. That will work, although that's not as general a solution to this class of problems.
Another example of this kind of error is assuming that data types that look similar in one program, the sending program, can be correctly received by another program receiving that data.
In this file, we have an array of objects with either strings or numbers as their values.
This leads to the temptation of sending this data, exactly as is to another program.
Therefore, when you try to dump that array, out of a program into another, it just won't work. You have to rely on some sort of data transfer medium like JSON and so on. At least most of the time.
Let me give you an example.
Here I've added a product to my cart. In order to pass the payment details to my client-side PayPal implementation. I need to get some data from my backend into this page.
Here you can see I've attached some JSON to this
window.oxnotes.payment variable, you can see it here.
window.oxnotes.payment, you can see it has
transactions, all that kind of stuff.
This corresponds to this bit of code in my back end. I have a
window.oxnotes.payment equals the HTML
raw version of this
@payment bit of JSON.
Let's see what happens if I just send a Ruby array to the browser instead of explicitly transforming it into JSON, as I currently do.
So I'm going to open up my payment gateway file. There we go.
.to_json on that.
So let me delete that and then go back to the browser and refresh and see what happens.
I get an
Uncaught SyntaxError. If I try to access the
window.oxnote.payment, it doesn't work.
You've got to take data interchange seriously when you're being rigorous about programming.
Ideally, you should use a first party tool like the
to_json method I have there, or some sort of other core language feature.
There are a lot of subtleties you might not expect when transforming data into JSON or some other sort of interchange format. For example, I was once tripped up when passing a string containing new lines to JSON, without using a transformer.
This is because JSON reserves the newline character internally and therefore you need to escape it. A bit of rigor here could have helped me avoid this issue.
Last but not least, I want to bring up something related, watch out for naming convention differences between languages.
For example, in a lot of backend code - Ruby included - snake_case is used.
This is a very common mistake.
For example, all incoming data that gets used, gets transformed using this function here,
Let me give you an example on the now fixed
You can see here, it's now in camelCase. It's
applicationContext instead of "application" underscore "context".
brandName, and so on and so forth.
In Oxbridge Notes I do that transformation on the front-end. However, in another one of my projects, I do this in the backend from the API.
Before returning any JSON, I call
parent: :camelCaseKeys, and you can see the definition for this here.
I haven't a strong preference for where this occurs, what matters is that it should recur transparently, and that you never have to really think about it.
That's all for today, see you next Sunday.