We talk a lot about marketing strategy on this blog. But today, we are getting technical.
In this post, I team up with WiderFunnel front-end developer, Thomas Davis, to cover the basics of server-side testing from a web development perspective.
The alternative to server-side testing is client-side testing, which has arguably been the dominant testing method for many marketing teams, due to ease and speed.
But modern web applications are becoming more dynamic and technically complex. And testing within these applications is becoming more technically complex.
Server-side testing is a solution to this increased complexity. It also allows you to test much deeper. Rather than being limited to testing images or buttons on your website, you can test algorithms, architectures, and re-brands.
Simply put: If you want to test on an application, you should consider server-side testing.
Let’s dig in!
Note: Server-side testing is a tactic that is linked to single page applications (SPAs). Throughout this post, I will refer to web pages and web content within the context of a SPA. Applications such as Facebook, Airbnb, Slack, BBC, CodeAcademy, eBay, and Instagram are SPAs.
Defining server-side and client-side rendering
In web development terms, “server-side” refers to “occurring on the server side of a client-server system.”
The client refers to the browser, and client-side rendering occurs when:
A user requests a web page,
The server finds the page and sends it to the user’s browser,
The page is rendered on the user’s browser, and any scripts run during or after the page is displayed.
The server is where the web page and other content live. With server-side rendering, the requested web page is sent to the user’s browser in final form:
A user requests a web page,
The server interprets the script in the page, and creates or changes the page content to suit the situation
The page is sent to the user in final form and then cannot be changed using server-side scripting.
In laymen’s (ish) terms:
When you visit a SPA web application, the content you are seeing is either being rendered in your browser (client-side), or on the server (server-side).
Basically, the page is incomplete upon arrival, and is completed within the browser.
If the content is being rendered server-side, your browser receives the application HTML, pre-built by the server. It doesn’t have to fill in any blanks.
Why do SPAs use server-side rendering?
There are benefits to both client-side rendering and server-side rendering, but render performance and page load time are two huge pro’s for the server side.
(A 1 second delay in page load time can result in a 7% reduction in conversions, according to Kissmetrics.)
All of which to say, with a complex application, client-side rendering can lead to sloooow initial load times. And, because client-side rendering relies on each individual user’s browser, the developer only has so much control over load time.
Which explains why some developers are choosing to render their SPAs on the server side.
But, server-side rendering can disrupt your testing efforts, if you are using a framework like Angular or React.js. (And the majority of SPAs use these frameworks).
The disruption occurs because the version of your application that exists on the server becomes out of sync with the changes being made by your test scripts on the browser.
NOTE: If your web application uses Angular, React, or a similar framework, you may have already run into client-side testing obstacles. For more on how to overcome these obstacles, and successfully test on AngularJS apps, read this blog post.
Testing on the server side vs. the client side
The original page loads, the content is hidden, the necessary elements are changed in the background, and the ‘new’ version is shown to the user post-change. (Because the page is hidden while these changes are being made, the user is none-the-wiser.)
As I mentioned earlier, the advantages of client-side testing are ease and speed. With a client-side testing tool like VWO, a marketer can set up and execute a simple test using a WYSIWYG editor without involving a developer.
A Quick Hack
There is a workaround if you are determined to do client-side testing on a SPA application. Web developers can take advantage of features like Optimizely’s conditional activation mode to make sure that testing scripts are only executed when the application reaches a desired state.
However, this can be difficult as developers will have to take many variables into account, like location changes performed by the $routeProvider, or triggering interaction based goals.
To avoid flicker, you may need to hide content until the front-end application has initialized in the browser, voiding the performance benefits of using server-side rendering in the first place.
Here is an example where we are testing a pricing change:
“Ok, so, if I want to do server-side testing, do I have to involve my web development team?”
But, this means that testing gets folded into your development team’s work flow. And, it means that it will be easier to integrate winning variations into your code base in the end.
If yours is a SPA, server-side testing may be the better choice, despite the work involved. Not only does server-side testing embed testing into your development workflow, it also broadens the scope of what you can actually test.
Rather than being limited to testing page elements, you can begin testing core components of your application’s usability like search algorithms and pricing changes.
A server-side test example!
For web developers who want to do server-side testing on a SPA, Tom has put together a basic example using Optimizely SDK. This example is an illustration, and is not functional.
In it, we are running a simple experiment that changes the color of a button. The example is built using Angular Universal and express JS. A global service provider is being used to fetch the user variation from the Optimizely SDK.
Here, we have simply hard-coded the user ID. However, Optimizely requires that each user have a unique ID. Therefore, you may want to use the user ID that already exists in your database, or store a cookie through express’ Cookie middleware.
Are you currently doing server-side testing?
Or, are you client-side testing on a SPA application? What challenges (if any) have you faced? How have you handled them? Do you have any specific questions? Let us know in the comments!
I started out as a web developer, and that’s now one part of what I do as a full-stack developer, but never had I imagined I’d create things for the desktop. I love the web. I love how altruistic our community is, how it embraces open-source, testing and pushing the envelope.
I love discovering beautiful websites and powerful apps. When I was first tasked with creating a desktop app, I was apprehensive and intimidated. It seemed like it would be difficult, or at least… different.
Over the last five years, Node.js has helped to bring uniformity to software development. You can do anything in Node.js, whether it be front-end development, server-side scripting, cross-platform desktop applications, cross-platform mobile applications, Internet of Things, you name it. Writing command line tools has also become easier than ever before because of Node.js — not just any command line tools, but tools that are interactive, useful and less time-consuming to develop.
If you are a front-end developer, then you must have heard of or worked on Gulp, Angular CLI, Cordova, Yeoman and others. Have you ever wondered how they work?
Chances are pretty good that you’ve worked with, or at least understand the concept of, server compression. By compressing website assets on the server prior to transferring them to the browser, we’ve been able to achieve substantial performance gains.
For quite some time, the venerable gzip algorithm has been the go-to solution for reducing the size of page assets. A new kid on the block has been gaining support in modern browsers, and its name is Brotli. In this article, you’ll get hands-on with Brotli by writing a Node.js-powered HTTP server that implements this new algorithm, and we’ll compare its performance to gzip.
Recently, I’ve been working on an isomorphic React website. This website was developed using React, running on an Express server. Everything was going well, but I still wasn’t satisfied with a load-blocking CSS bundle. So, I started to think about options for how to implement the critical-path technique on an Express server.
This article contains my notes about installing and configuring a critical-path performance optimization using Express and Handlebars. Throughout this article, I’ll be using Node.js and Express. Familiarity with them will help you understand the examples.
I had been doing server-side programming with Symfony 2 and PHP for at least three years before I started to see some productivity problems with it. Don’t get me wrong, I like Symfony quite a lot: It’s a mature, elegant and professional framework. But I’ve realized that too much of my precious time is spent not on the business logic of the application itself, but on supporting the architecture of the framework.
I don’t think I’ll surprise anyone by saying that we live in a fast-paced world. The whole startup movement is a constant reminder to us that, in order to achieve success, we need to be able to test our ideas as quickly as possible. The faster we can iterate on our ideas, the faster we can reach customers with our solutions, and the better our chances of getting a product-market fit before our competitors do or before we exceed our limited budget. And in order to do so, we need instruments suitable to this type of work.
Things often come full circle in software engineering. The web in particular started with servers delivering content down to the client. Recently, with the creation of modern web frameworks such as AngularJS and Ember, we’ve seen a push to render on the client and only use a server for an API. We’re now seeing a possible return or, rather, more of a combination of both architectures happening.
It is a way to create reusable front-end components. Plain and simple, that is the goal of React.
What Makes It Different?
React is often described as the “V” in an MVC application. But it does the V quite differently than other MV* frameworks. It’s different from things like Handlebars, Underscore templates and AngularJS templates. React operates on the concept of a “virtual DOM.” It maintains this virtual DOM in memory, and any time a change is made to the DOM, React does a quick diff of the changes, batches them all into one update and hits the actual DOM all at once.
React exposes a method called React.renderToString(). This method enables you to pass in a component, which in turn renders it and any child components it uses, and simply returns a string. You can then take that string of HTML and simply send it down to the client.
React.render attaches your <HelloWorld> component to the body. Naturally, that could be any element there. This causes the component’s render method to fire, and the result is added to the DOM inside the <body> tag.
With a React component, whatever you pass in as attributes — say, <HelloWorld message="world" /> — you have access to in the component’s this.props. So, in the <HelloWorld> component, this.props.message is world. Also, look a bit closer at the JSX part of the code:
You’ll notice first that you have to wrap the HTML in parentheses. Secondly, this.props.message is wrapped in braces. The braces give you access to the component via this.
Each component also has access to its “state.” With React, each component manages its state with a few simple API methods, getState and setState, as well as getInitialState for when the component first loads. Whenever the state changes, the render method simply re-renders the component. For example:
var Search = React.createClass(
<input type="text" onChange=this.changeSearch />
<span>You are searching for: this.state.search</span>
var text = event.target.value;
React.render(<Search />, document.body);
In this example, the getInitialState function simply returns an object literal containing the initial state of the component.
The render function returns JSX for our elements — so, an input and a span, both wrapped in a div. Keep in mind that only one element can ever be returned in JSX as a parent. In other words, you can’t return <div></div><div></div>; you can only return one element with multiple children.
Notice the onChange=this.changeSearch. This tells the component to fire the changeSearch function when the change event fires on the input.
The changeSearch function receives the event fired from the DOM event and can grab the current text of the input. Then, we call setState and pass in the text. This causes render to fire again, and the this.state.search will reflect the new change.
Many other APIs in React are available to work with, but at a high level, what we did above is as easy as it gets for creating a simple React component.
With React, we can build “isomorphic” apps.
i·so·mor·phic: “corresponding or similar in form and relations”
This has already become a buzzword in 2015. Basically, it just means that we get to use the same code on the client and on the server.
This approach has many benefits.
Eliminate the FOUC
With AngularJS, Ember (for now) and SPA-type architecture, when a user first hits the page, all of the assets have to download. With SPA applications, this can take a second, and most users these days expect a loading time of less than two seconds. While content is loading, the page is unrendered. This is called the “flash of unstyled content” (FOUC). One benefit of an isomorphic approach to building applications is that you get the speed benefits of rendering on the server, and you can still render components after the page loads on the client.
The job of an isomorphic app is not to replace the traditional server API, but merely to help eliminate FOUC and to give users the better, faster experience that they are growing accustomed to.
One big benefit is being able to use the same code on the client and on the server. Simply create your components, and they will work in both places. In most systems, such as Rails6, ASP.NET MVC7, you will typically have erb or cshtml views for rendering on the server. You then have to have client-side templates, such as Handlebars or Hogan.js, which often duplicate logic. With React, the same components work in both places.
Server rendering allows you to send down the barebones HTML that a client needs to display a website. You can then enhance the experience or render more components in the client.
Delivering a nice experience to a user on a flip phone in Africa, as well as an enhanced experience to a user on a 15-inch MacBook Pro with Retina Display, hooked up to the new 4K monitor, is normally a rather tedious task.
React goes above and beyond just sharing components. When you render React components on the server and ship the HTML down to the client, React on the client side notices that the HTML already exists. It simply attaches event handlers to the existing elements, and you’re ready to go.
This means that you can ship down only the HTML needed to render the page; then, any additional things can be pulled in and rendered on the client as needed. You get the benefit of fast page loading by server rendering, and you can reuse the components.
Creating An Isomorphic Express App
Express138 is one of the most popular Node.js web servers. Getting up and running with rendering React with Express is very easy.
Adding React rendering to an Express app takes just a few steps. First, add node-jsx and react to your project with this:
That’s it for the server code. Let’s look at what’s necessary on the client side.
For React .jsx files, you’ll just need to configure your webpack.config file a bit in order to compile all of your jsx components.
Getting started with Webpack is easy:
npm install webpack -g # Install webpack globally
npm install jsx-loader --save # Install the jsx loader for webpack
Next, create a webpack.config.js file.
var path = require("path");
module.exports = [
test: /.jsx$/, loader: "jsx-loader?harmony"
// You can now require('file') instead of require('file.coffee')
extensions: ["", ".js", ".jsx"],
Let’s break this down:
This is the main file that will load your other files using CommonJS’ require syntax by default.
The module object is where you set up “loaders.” A loader simply enables you to test for a file extension and then pass that file through a loader. Many loaders exist for things like CSS, Sass, HTML, CoffeeScript and JSX. Here, we just have the one, jsx-loader?harmony. You can append options as a “query string” to the loader’s name. Here, ?harmony enables us to use ECMAScript 6 syntax in our modules. The test tells Webpack to pass any file with .jsx at the end to jsx-loader.
In resolve we see a few other options. First, extensions tells Webpack to omit the extensions of certain file types when we require files. This allows us just to do require("./file"), rather than require("./file.js"). We’re also going to set a root, which is simply the root of where our files will be required from. Finally, we’ll allow Webpack to pull modules from the node_modules directory with the modulesDirectories option. This enables us to install something like Handlebars with npm install handlebars and simply require("handlebars"), as you would in a Node.js app.
var React = require("react"),
App = React.createFactory(require("components/app"));
if (typeof window !== "undefined")
window.onload = function()
We’re going to check that we’re in the browser with the typeof window !== "undefined". Then, we’ll attach to the onload event of the window, and we’ll call React.render and pass in our App(). The second argument we need here is a DOM element to mount to. This needs to be the same element in which we rendered the React markup on the server — in this case, the #content element.
React is one of the first of what are sure to be many frameworks that enable this type of behavior. Ember’s developers are already working on isomorphic-style applications as well. Seeing how this all works out is definitely going to be fun!