Examples of well designed e-commerce sites

What makes a good e-commerce website? There are a myriad of different things – good product information, ability to buy products, information on shipping, good security. Of course a good design all is important, melding all these things together. The first website I think is well designed is Wild Man Ricing. Based in Manitoba, it sells wild rice, and related products, actually only six products total, but this website is aesthetically pleasing *and* functional. Straight away you get a sense of where the store is located from the splash image on the main page. There is a lot of information about wild rice, links to articles about the business, contact information, and a simple link to the cart.

An aesthetically pleasing and functional website.

Product pages are packed full of information, which is very clear. All prices include shipping, which makes that very simple – it is also clear that the minimum order is $60.00. There is a large block that allows clients to ask questions, should they have any. There is only one downside, and that is that “Add to Cart” assumes a 1 lb bag, and changes have to be made in the Cart, which opens on another browser tab. There is also no search facility, but frankly this is one of those rare cases where everything is so easy to find that there may be no need to have a search capability.

An example product page

It is a great example of an e-commerce website for fewer than 10 products that is actually designed in a thoughtful, user-friendly fashion. Another website I quite like is Gourmet Sauvage, based in Quebec, a purveyor of “non-timber forest products“, e.g. dried mushrooms, birch syrup etc. The main webpage is clean and fresh. It makes good use of a hero image, which changes based on language – for example in French it displays their book, FORÊT, which is only available in French. The language options FR/EN are easily accessible, as is information on delivery costs, search function, and shopping cart. Product pages are clear, informative and easy to use.

The other thing that both these websites do is use the .ca domain extension, immediately showing visitors to their website that they are Canadian companies. A third store I really like, and one built on Shopify is The Newfoundland Store. Based in Toronto, this store sells food products from Newfoundland.

The front page makes use of a hero image which spans the entire page. It invokes the feel of a Newfoundland rugged coastline together with the classic jellybean colours of the houses. It has a somewhat nostalgic feel, in part to the simple use of “Welcome Home” on the page, likely a nod to the fact that many visitors to the website may have lived in Newfoundland at some point in their lives. The top menu shows a nice clear set of options with product information, and the trio of “must-haves”, account info, search and cart.

Some big-box retailers still don’t get e-commerce

As much as small retailers shouldn’t discount the need for an online presence, some Canadian big box retailers don’t always get it right either. Why? I don’t quite understand it, its not like they don’t have the resources. What are some of the biggest issues with e-commerce in Canada?

The first problem seems to be the concept of delivery. A case in point is Canadian Tire. For years they have fumbled with their e-commerce presence. For a long time it was possible to look on their website and find a product in-store, but not for delivery. Then when you went to the store, it was likely they didn’t have any stock, or couldn’t find the product. Now there seems to be basic delivery, but not for every item. They do have in-store pick-up, which I imagine isn’t that convenient for people living far from a Canadian Tire but would like to buy something. Below is a case in point, one product which will “Ship to Home”, another which won’t (and when I tried it another time to the same address, it said the Blue Shop Towels couldn’t shipped either). The problem here is that if you have large distribution warehouses, product availability and shipping should not be a big deal, really it shouldn’t.

Shopping at Canadian Tire

Another retailer, which I often shop at is Home Depot. Here I get that some items, i.e. lumber have to be delivered by truck, and some things are awkward to ship, e.g. large, heavy, strangely shaped items. But here the problem is sometimes the “FREE Pick Up”. Some items are clearly available for pick-up, others aren’t. It makes no logical sense. Below is a case in point, comparing two products, both of which are sold in the store, and have stock. The “Heavy Duty Rubber Stair Tread” on the left is not available for delivery, nor can it be picked up in-store. The “Heavy Duty Rectangular Rubber Door Mat” shown on the right, *is* available for both delivery and pick-up in-store (and it weights a bunch more). So I literally have to go in the store to buy the tread, but the door mat they will bring to my car. Does this make any logical sense?

Shopping at Home Depot

Big-box food retailers aren’t much better in some cases. I ordered some stuff from Walmart, who do deliver, but by the time I had it delivered four days later, some items weren’t available. Why?, because they were picking them off-the-shelf in-store. This is fine for pick-up, but unacceptable for delivery.

One of the best e-commerce experiences I have had is Sobey’s Voila delivery service. Here Sobey’s has built a 250,000 square foot state-of-the-art robotically-automated temperature-controlled Customer Fulfilment Centre (CFC). So the available stock is the available stock, and the system will tell you if something is not available once in the cart (if you have left the cart too long). So there is no “product substitution” problem. I get that this is a purpose-built facility, but could Canadian Tire not do the same? It’s not like product fulfillment is rocket science.

The irony of e-commerce

During the pandemic “shut-downs” there have been many businesses that have invariably been forced to temporarily shut-down. There have been others who have opened given new opportunities. Others still have pivoted their businesses so to face the new challenges. I feel bad for some businesses that inherently can’t change their method of business, e.g. barbers. There are some businesses however who rely solely on foot-traffic, when in reality they sell products that could be delivered locally or shipped online. The irony of e-commerce is that some shops barely have a website that communicates what they do, let alone some form of e-commerce setup.

Now food stores have learned this very quickly. Places that I use to shop in person, like fishmongers and butchers have very quickly set-up an exceptional e-commerce systems that actually work extremely well… especially for products that change weekly, and have an expiry date. It is 2020, and it you have a store that sells non-perishable products, you should have an online presence, and by that I mean an e-store. There is no excuse, and the start of this pandemic should have driven that message home. But we are not just talking about pandemic’s, we are also talking about a broader customer base. If you’re selling boutique honey from the Vancouver Island, I may actually be interested in buying some? Why would you limit your reach?

Here are some mistakes people make with websites, and e-commerce.

  • Having websites that have issues from a usability point-of-view. Look I get that not everyone is a designer. As an example, consider Wild Gaspe from Quebec, which sells wild foods like mushrooms, herbal teas, and seaweeds. There products are *awesome*, and they have a good e-commerce site, but their main site is a bit distracting. Having said that they do offer French/English versions of both sites, which is extremely good.
  • Not having an English version of a website. Again, I get it if your clientele is local, but limiting a website to French means you are ignoring 79% of the Canadian population – that’s a lot of customers. Yes, most Canadians should have enough French to still order from a French-only website, or one can use Google translate (which these days works extremely well). Conversely, not having a French-language version of an English-Canadian website isn’t great either.
  • E-commerce sites that don’t make sense. Consider this French-only site, Ducs de Montrichard, for duck products from Quebec. Good products, lack-luster website. The products exist, and I would order in French, because I can figure out that “Ajouter au panier” means “add to cart”, however after I add stuff I can’t find the cart! No icon, no nothing. Maybe it only exists on certain browsers, or they removed the ability to buy online?
  • Some websites just suffer from a little bit of design anxiety. Too many or inappropriate fonts, images that don’t work, wonky screen transitions, and navigation problems. I like the overall design of Queen Bees Farms, they have good product information, and offer delivery. However, when I added something to the cart, it showed the cart contents with the same colour as the webpage background, not an ideal scenario. When I clicked on the shopping bag later, same deal.
Colour design issues.
  • Browser issues. So things don’t work on old browsers, or indeed other browsers. An example of this is FrenchFoodsMarket. Works great on the latest version of Safari… not so much on older versions. Check out the logo on two lines, or the fact that when I click on a menu item nothing happens. (They do have a good selection of French goods, one of the few in Canada available online).
Some things don’t work in old browsers
  • No product information. No matter how good a website is, not having a store is one thing, no product information is a complete other story. Here is an example that has neither online store or product information (at least that I could find) – Babe’s Honey Farm. I love the design of the main page, it’s quite pleasant, and I get that honey is possibly harder to ship than most things. However, apart from a couple of pictures relating to the nine (?) different honey’s they offer, there is no product info. I wanted to know a bit about each of the different types of honey they offer, especially considering they have been producing honey for 75 years! (especially as I was looking to buy honey as Christmas presents).

I get that not everyone wants the hassle of e-commerce, but we live in different times. As a local bookstore it may be difficult to warrant creating an e-commerce store for an ever changing inventory – there are however websites like as Bookshop, (UK and US at the moment) which endeavour to improve the lot of independent bookstores. If you have 10 products though, and want to make an impact then you need a functional, well-designed e-commerce site. Yes, it use to be hard to set-up a website, but with systems like Shopify, making an e-commerce website is trivial, literally. Sure you may have to do some work, e.g. taking some photographs of stock, but imagine the potential.

If you want to do a PhD, here are some things you should know

I am writing this article because of the emails that swamp my inbox on a weekly basis asking me to “kindly consider being my supervisor for a PhD“. There was a time when people pursued a PhD because they were engrossed in a particular topic, and wanted to continue getting degrees. Some would argue that people who pursue a PhD need to be smart, but I don’t necessarily share that opinion (intellectual smart or life-smart?). Now people seem to largely use graduate programs for other reasons. Some people I have met over the years even think that because they have a PhD they are special. Nope.

Before you even think about asking someone to be your supervisor, consider the following things:

  • The production of PhDs has far outstripped demand for university faculty. In Canada every year approximately 7000 people are awarded PhDs. Only 20% of PhDs work as university faculty, and only 2% of jobs in Canada require a PhD (ref).
  • A PhD may offer no financial benefit over a master’s degree. It can even reduce earnings. PhDs in math, computing, social sciences, and languages often don’t earn not much more than those with master’s degrees.
  • In some fields, the pay isn’t any better than a Bachelors degree. In a recent study done by Statistics Canada, they showed the median salary earnings (five years after graduation) for all fields of study for female and male PhD recipients. Computer Science specific it was $98,484 (male), and $73,687 (female). The study also showed that science doctoral graduates earned the least (go STEM!). Here are links to information on masters and bachelors median earnings. Some stats summarized below:
fieldBachelors degreeMasters degreePh.D.
Computer Science (male)$86,076$89,821$98,484
Computer Science (female)$71,573$77,039$73,687
Computer Engineering (male)$78,905$86,260$103,657
Computer Engineering (female)$72,911$65,752n/a
Comparing salaries in Canada

For some more context, here are some good articles on the subject:

A PhD is not just something that you do to increase your salary outlook, or “because I want to do research“. Those things just aren’t realistic reasons. Being a good researcher is well and good, but the primary mission of most universities is teaching. Unlike a PhD which a research focused, an academic job involves teaching a number of classes each year (increasingly large classes). Teaching is a noble profession, but ask yourself – can you teach? To do both effectively you have to work 50-60+ hours a week, not exactly a balanced way of life. It is rare In Canada there just aren’t that many academic jobs available anymore, resulting in part from hiring freezes, and faculty that don’t retire.

Bottom line? Do a PhD for the right reasons. In many cases an academic job is a pipe-dream, unless you are prepared to work in a remote university somewhere, or spend years doing sessional work (or have a *huge* research portfolio).

The dark art of defensive programming

Defensive programming is a preemptive process designed to help validate preconditions, postconditions and invariants in a program by identifying sections of a program which could be encapsulated in a “self-checking” wrapper. This may involve checking arguments to a function for validity before execution of the body of the function.  For example incorrectly constrained input is one of the most common forms of software failure. This relates to inspecting inputs for validity before processing continues. This is a notion which is often overlooked, students are not exposed to a culture of validating input when they write an algorithm. Defensive programming can be used to check the validity of input.

Defensive programming can also be used to validate input to functions. Two commonly overlooked errors relate to “divide by zero” and log(0). It It is easy to use defensive programming to “rewrite” these functions. The code below shows the log10() function of C reconstructed to trap the calculation of log10(0).

double Log(double x){
   if (x == 0)
      return 0;
   else
      return log10(x);
}

It can also be used in situations where the programming language contains harmful constructs such as C’s dangling-else problem. The “dangling else” is a very common and subtle logic error found in the flow of control of a nested if statement. Since a compiler ignores indentation,  it matches an else clause with the most recent unmatched then clause.

if (a > 0.0) 
   if (a > maximum)
      maximum = a;
else
   minimum = a;

The following small change in the if statement solves the problem (use braces to enforce an association:

if (a > 0.0){
   if (a > maximum)
      maximum = a;
}
else
   minimum = a;

Defensive programming might advocate that an if-then statement in C should always be coded with braces, even when the then part contains only a single statement. Other languages do a better job of handling these sorts of issues. Fortran (gfortran in this case), if provided with a scenario where log(0.0) has to be calculated, will assign the value -infinity to the resulting variable, and continue progressing through the program. For x/0.0, it gives the value infinity. At least it provides some semblance of feedback, and doesn’t crash the program. But in reality, checking that a denominator is zero before a division is always a good idea.

The best help with defensive programming is offered by languages such as Ada, which offer exception handling (which is not really a surprise). Ada makes *heavy* use of exceptions, especially for checking data consistency at runtime. Ada tries very hard to protect the programmer against themselves. If you do nothing in Ada, something like a divide-by-zero will automatically throw an exception. To be pro-active it is easy to raise an exception if the denominator is zero. Here is an example:

procedure validate is
   a, b, c : float;
begin
   get(a); get(b);
   if b = 0.0 then
      raise Constraint_Error with "Division by 0";
   end if;
   c := a / b;
   put(c);
end validate;

Languages that integrate defensive programming into their core functionality often offer a programming environment for novice programmers. Ada also checks type ranges, array boundaries, null pointers, and functions not returning a value.

Sloppy coding? Try some defensive programming

Yesterday, there was an article that ran in the news, Millions of smart devices vulnerable to hacking. The basic gist here is that many internet-linked smart devices have been designed with little or no thought about security. We’re talking things like remote-controlled temperature sensors and cameras. The researchers named the 33 flaws they discovered as Amnesia33. Surprised? I’m not.

The main issue cited seems to be sloppy programming, i.e. bad coding practices, such as an absence of basic input validation and shotgun parsing. When I taught introductory programming, I made a concerted effort to have people make sure their programs validated input. I’m sure many students thought this as being too trivial. Sometimes it’s because for a small program code validation can be as long as the core functionality of the program itself. I posed the practice through the art of defensive programming.

Defensive programming is not something you will see in most introductory programming courses, but it is here where good programming practices are formed. Writing code is not an individualistic pursuit – the algorithm, sure. The code should conform to some standard, and that includes things like style, and validation. Defensive programming sssumes that Murphy’s Law is true, and that “the worst possible thing will happen, at the worst possible moment”. With software this is always the case, and the more complex a piece of software is, the more likely at some point something will not go as planned. You are basically defending against program errors, both those related to input, and those within the program itself such as variable type bounds.

Do not trust anything, verify everything.

Some students likely think simple things like input validation is a joke. But even in a simple program which adds two user-entered numbers there is scope for problems to occur. Consider the following C code:

#include <stdio.h> 
int main(void){
   int a, b, c;
   printf("Enter two numbers: ");
   scanf("%d%d", &a, &b);
   c = a + b;
   printf("%d\n",c);    
   return;
}

If the user enters two integers, the program will run fine, it the first number is an integer and the second a real, the program will run fine (because the real will be truncated), but enter a real first, and the program won’t produce the right answer. Trivial? Yes. But if ignored small errors in small programs become inconspicuous errors in larger programs. But won’t the user enter the right information? Not necessarily, even if you asked them to enter an “integer”. Programmers must defend the user against themselves. In this case that means validating the input is an integer (which C does not do for you).

There are of course downsides to defensive programming consumes resources, like the consumption of resources, or the program running marginally slower. Conversely it can save hours or days of debugging time.

Code that runs properly, but diminutively slower is better than code that is fast, but occasionally collapses in a shower of brightly coloured sparks.

In reality the current environment of sloppy coding should be no surprise to anyone. People tend to forget about the simple things in programming, largely because sometimes they are bogged down by the complexities of languages, or even algorithms. Or they wrongly assume the language will handle improper data. I doubt very much that introductory programming courses in most institutions really teach things like the importance of input validation to any great extent. The fact that people are now advocating for this in terms of teaching a “security concept” in first year programming courses is somewhat ironic considering the field of security only exists because software is not typically built in any way that could be considered perfect. (Could it ever be perfect, that is the $1,000,000 question?).

In the 1960s you had to implement your own compiler

Programming now is easy because you can just download a compiler in most languages. Imagine if you had to build your own? Let’s consider Algol 60. As the name suggests, it appeared in 1960 as a report, “Report on the Algorithmic Language ALGOL 60” [1], edited by Peter Naur. Whole books [2] were dedicated to implementing Algol 60 compilers. Given a specification, people derived their own compilers, based on their needs, and the hardware platform they had. To date there have been over 70 differing implementations and derivatives of Algol 60. One version even appeared behind the iron curtain as the language ALGAMS. Part of the problem was the multitude of hardware platforms, all requiring their own object code. For instance, the first supposed commercial ALGOL compiler was Elliott ALGOL, released in 1962 to run on an Elliott 803 computer.

The biggest problem was of course compatibility. That is why if you browse programming books of the 1960s and 1970s, most of which use Algol as an algorithm description language, no two programs will be the same. Added to this was the problem of I/O – the specification for ALGOL did not define any I/O constructs, so each implementation defined their own. Imagine if the same had happened to C, or Java?

[1] Naur, P., et al. “Report on the Algorithmic Language ALGOL 60“, Communications of the ACM, 3(5), pp.299-314 (1960)
[2] Randell, B., Russell, L.J., ALGOL 60 Implementation: The Translation and Use of ALGOL 60 Programs on a Computer, Academic Press (1964)

Recursion in APL

APL is the acronym for “A Programming Language”, designed as a hardware description language at IBM by Ken Iverson in 1962.  It was later developed in collaboration with A.D.Falkoff, and appeared as an interactive language in 1967, and was very popular with mathematicians and engineers, due to its ability to express mathematical algorithms concisely. Dijkstra didn’t have many good things to say about APL (but at least he was honest).

“APL is a mistake, carried through to perfection. It is the language of the future for the programming techniques of the past; it creates a new generation of coding bums”.

Dijkstra (“How do we tell truths that might hurt”, 1975)

I never liked APL, largely because it is a very symbolic language based on mathematical notation, and as keyboards shed their symbols, it became harder to realistically code in as a language. It did however allow recursion, but texts of the period didn’t really give much credence to it. Even in the 1980s, texts gave no more than a page to the concept. In Leonard Gilman’s APL: An Interactive Approach, published in 1984 the author commented that recursion “is an intriguing topic for most neophyte computer scientists“, including a program for deriving combinations of numbers, and the ubiquitous factorial for entertainment purposes.

Here is a version of Fibonacci written in APL [1]:

    ∇ NUMBER⟵FIB N
[1] NUMBER⟵1
[2] ⟶0 IF N=1
[3] ⟶0 IF N=2
[4] NUMBER⟵(FIB N-1)+(FIB N-2)
    ∇

The program is called FIB, and it produces a NUMBER, which is the Nth in the Fibonacci sequence. In line [1] the NUMBER is assigned the value 1. In lines [2] and [3], if N=1 or N=2, the program stops with 1 as the value returned. If N is larger then 2, then NUMBER is assigned the result of (FIB N-1) and (FIB N-2), invoking a double recursion.

The problem with APL lay in its mathematical nature, although there were some constructs, such as ⟵ for assignment that are inherently more usable than the = used in most languages.

[1] Peelle, H.A., “Euclid, Fibonacci, and Pascal – recursed!”, Int. J. Math. Edu. Sci. Technol., 6(4),pp.395-405 (1975).

Recursion – Fortran bends the rules

Until Fortran formally allowed recursion in Fortran 90, there were many ways of skirting the issue. One of the more interesting was that posed by John Morris in 1969 [1]. Fortran generally didn’t offer recursion as part of the languages abilities because recursive function calls required special treatment. The simplest solution, at least for the purpose of recursive mathematical functions is to turn the recursions into iterations. A factorial doesn’t need to be calculated in a recursive manner.

So things don’t convert very neatly into iterations, largely because their recursive definition is not easily thwarted. Instead of true recursion, which stores the return address in a stack, Morris suggested storing the arguments of the function in one or more stacks. He provided solutions to three such problems: (i) Ackerman’s function, (ii) a combinatorial problem, and (iii) calculating Kendall’s rank correlation coefficient tau. Ackermann’s function is of no real value, except for testing the ability of systems to handle hungry calculations (at least in early days). Here is the recursive function:

If m=0 then ACKER(0,n) = n+1
If m≠0 and n=0 then ACKER(m,0) = ACKER(m-1,1)
IF m≠0 and n≠0 then ACKER(m-1,ACKER(m,n-1))

Here is Morris’s “recursive” version of the Ackerman function:

      FUNCTION ACKER(M,N)   
      COMMON /SCTCH/ STAK(3000)   
      ACKER=0.   
      IF(M.LT.0.OR.N.LT.0) RETURN   
      ACK=0.   
      XM=M   
      XN=N   
      INDEX=0
    7 IF(XM.GT..01) GO TO 3   
      ACK=XN+1   
      IF(INDEX.GE.1) GO TO 4   
      ACKER=ACK   
      RETURN
    3 IF(XN.GT..01) GO TO 5   
      XM=XM-1.   
      XN=1.   
      GO TO 7 
    5 IF(INDEX.LT.3000) GO TO 6   
      WRITE(*,*) 'STACK CAPACITY EXCEEDED'   
      ACKER=0.   
      RETURN 
    6 INDEX=INDEX+1   
      STAK(INDEX)=XM-1.   
      XN=XN-1.   
      GO TO 7 
    4 XM=STAK(INDEX)   
      XN=ACK   
      INDEX=INDEX-1   
      GO TO 7   
      END

The program stores the values of m-1 in the stack STAK, while evaluating the right-hand argument in the last form of the function, then returning to the stack to retrieve the value. It is in essence, a form of recursive iteration.

[1] Morris, J., “Programming recursive functions in Fortran”, Software Age, 3(1), pp.38-42 (1969)

Recursion – the trouble with early Fortran

While various ALGOL compilers allowed recursion, Fortran didn’t, or at least not in the 1960s or 70s. Basically in Fortran a subprogram could not call itself, neither could a subprogram A call subprogram B, which then calls subprogram A.

The design of Fortran never considered the possibility of implementing recursion. Even though Fortran II evolved at the same time as Lisp, it may have been a concept too early for inclusion into Fortran. Both direct and indirect recursion were forbidden until Fortran 90. But why did Fortran have issues with recursion? When a subroutine or function is called, its return address is stored. Control is handed back to this address when the return statement in the function being called is invoked. In early versions of Fortran, the subroutine or function could only store one return address at a time. Recursion is therefore not possible, because a stack of return addresses would be required. In addition, if the function or subroutine contains local variables, then these too must be stored on a stack, so they may be restored when the recursive call is ended. 

In 1963 James Ayers wrote a small article in Communications of the ACM, “Recursive Programming in Fortran II”, describing a technique that would “…draw the poison from the Fortraner’s wounds by allowing them to recurse to their complete satisfaction.”. But these techniques were specific to the compiler being used. IBM’s System/360 Fortran IV language manual of 1966 identified recursion as an “invalid statement function definition”.

BAD(A,B)=A+B+BAD(C,D)      (recursive definition not permitted)

In 1969 Herbert Kugel wrote an article in Software Age [1] exploring the notion of recursive subroutines in Fortran on IBM’s System 360, which did allow recursion (however it used two assembly language subroutines, SET and RESET, that dealt with the problem of saving and restoring the initial contents of the general registers).

One of the key differences between Algol 60 and Fortran was storage allocation, and this is necessary in order to appreciate the difference between the languages in their treatment of subprograms. Fortran was designed so that the storage requirements of the program were calculated at run-time. The storage requirements were therefore fixed. In Algol conversely, storage requirements are not calculated at compile time – the language makes allowances for requisitioning storage space at runtime. This convergence means that some capabilities available to the Algol 60 programmer were not available to the Fortran programmer. 

[1] Kugel, H.C., “An experimental approach to recursive fortran subroutine programming under operating system 360”, Software Age, 3(12), pp.14-17 (1969)