Quality-first UI Development

In the same spirit as mobile-first and offline-first, I want to write about quality-first.

It merely means: start with quality in mind and don’t make it an afterthought.

If we say we are professional developers, the quality of the systems we produce is our responsibility.

Does this apply to you? Ask yourself these two questions:

  1. Does the majority of the work you do revolve around software development? Not coding, but development
  2. Are you paid to do it?

If you answered "yes" to both these questions, congratulations, you are a professional! 🎉

You can let the imposter syndrome go, you made it!

No one knows everything. Not even close. We're all still on the journey to master this craft. Everyone started with zero knowledge, zero experience.


Now that we've established that quality is our responsibility let's talk about it.

We're going to look at quality from two perspectives: UX & DX (user experience and developer experience)

Instead of getting hung up about the correct definitions here, in light of this post, they mean quality from the perspective of the end-user and the developer.

I would argue that good DX goes a long way in helping to produce good UX. If you don't agree, let me know on Twitter.

UX

We all agree UX is essential. If users have a terrible experience, they'll leave, and without users, what's the point in what we build?

To produce a good user experience, we need to understand our users and how they use our products.

Some questions we need to ask about our users:

  • Who are they?
  • From where are they?
  • Which browsers do they use?
  • Do they have any disabilities?
  • Are they native English speakers?
  • What type of devices are they using?
  • How stable are their internet connections?
  • What time of day do they typically use the product?

You get the point. Most of these you probably already think about, but if you're not, now is an ideal time to start exercising those empathy muscles.

DX

Developer Experience is focusing on the developers' quality of life during the development process. A lot of DX comes down to tooling. These tools include the frameworks we choose to use, our IDE's, browser developer tools, plugins, CI/CD processes and more.

If you are a team lead, it is your responsibility to set your team up for a win. Make it hard for teammates to produce poor quality by adequately setting up build processes, linting, tests and deployments.

Accessibility

Accessibility is sometimes a weird subject, and people get very vocal and defensive about it. It doesn't need to be that way.

When you hear accessibility, most people think about blind people, but it is about a lot more. It is about real people with real lives and how we, as developers cater to these humans, our users. It is about the web being accessible to as many people as possible. No other platform can do that.

According to the World Bank Group, 1 billion people, or 15% of the world's population, experiences some form of disability, with more than 100 million of them with a severe disability.

To put that in perspective: IE11 currently has 2.75% global usage, and we're building websites that work in it. If we care about this small percentage, why would we not cater to the 15% of people with disabilities? I'm not saying that the full 15% of people with disabilities use the web, but the good news is that these users aren't using IE, that's for sure. 🤗

We should not charge extra for accessibility. It is part of the quality that we, as professionals, should deliver.

Some things to consider:

  • Can your app be navigated with only a keyboard?
  • Is your app friendly to screen readers used by people with sight impairments?
  • Does your app assume users can distinguish options solely based on colour?
  • Is there enough contrast between your UI elements (text vs background colours, etc.)?
  • Does the user prefer reduced motion?
  • Do all your videos provide subtitles?

What can we do?

  • Set up tools like ESlint that make us aware of obvious accessibility issues in our code
  • Learn proper HTML semantics
  • Learn about ARIA
  • Use browser and CLI tools to test accessibility issues
  • Play with a screen reader
  • Test navigating your app using only your keyboard
  • Speak up when you notice UX that could be better. Don't be a developer that only do what is given to you. Use your beautiful brain; you're a professional, remember.

Security

We're not all working for the CIA, but there are some simple principles we can put in place to vastly improve security in our apps.

Things to consider

  • Are all your pages served over HTTPS? There isn't a good reason for your app not to use it anymore. Most hosting providers provide an SSL certificate for free. More than that, many web API's and features like Service Workers only work for sites served over HTTPS.
  • Use HTTP headers like HSTS and CSP to restrict your site from ever running over plain HTTP or loading any other resources on your page. With something as basic as this, you can almost eliminate most technical security issues.
  • Is your app taking any user input? If so, make sure you sanitize everything. A good example is when writing HTML to the DOM using innerHTML. If the HTML string you're writing is coming from a user, they can effortlessly inject a script into your app. Expect the worst. It is suitable for a developer to be slightly cynical.
  • Most of the time, the most significant gap in our security plans, is us, the humans involved. A solid philosophy to follow when it comes to security is the "principle of least privilege". It simply means to provide the minimum permissions needed to accomplish a task and nothing more. For instance, if a user should only be able to add a new entry, don't give them delete permissions. At startups, when iterating quickly, this can be unnecessary red tape for your internal systems, but think about it and make a decision that suits your team the best.

Performance

Performance is probably one of the most critical aspects of quality software that we can look at, both from a user's and a developer's point of view. Numerous studies have reported the financial gains, or losses companies have when seconds are shaven off from their app load times. We should, therefore, optimize for performance as much as we can.

We can also look at performance from the point of delivery: how long development of a feature takes. In other words, efficiency. Feature development is an area where faster isn't always better for quality. On the other hand, slower also does not necessarily mean better quality. Tools can help us.

Let's face it; we are not good at repetitive tasks. When you're moving fast, you can not possibly remember to check everything. Set up your tools beforehand to catch apparent mistakes, and when things slip through the cracks, update your tools for the next time. I use the term "tools" very loosely here. These tools can be code linters, unit tests, spell checkers, release checklists, etc.

I'm personally not someone that would argue that a Computer Science degree is necessary to be a top-class developer. One thing I would say is that everyone should understand Big-O notation. I'm not talking about heavy computer science theory, but as a way to think about the code you just wrote and how well it performs. Understand the difference between time complexity versus space complexity. A function can have O(1) time complexity, but be so extremely heavy in terms of space complexity that your user's device runs out of memory. An O(1) function is useless in that scenario. With this said, Big-O is something to be aware of - if you're thinking about it, you are already winning.

Communication

I'm not going to say much about this, because we hear it all the time, communication is essential in most areas of our lives. Instead of quoting another cliché, let's talk about one or 2 places where communication is important in our day to day jobs as developers:

Code Reviews

Few communication tools make such a big difference, in the quality that we produce, as code reviews. I want to encourage you to note how you communicate on code reviews. As the person creating a pull request: are you clear about the intent of your code change? Use commit messages, PR descriptions, or before and after screenshots. These things go a long way in giving your reviewers some much-needed context.

From a reviewers point of view, are you "listening" (paying attention) to the proposed code change? When you give feedback, are you merely nitpicking at a preferred code style or are you aiming to add value? A good thing to keep in mind is the concept of "Radical Candor". It boils down to this: speak plainly but from a place of caring about the person and the product. It is healthy to disagree with and question a code implementation, but it is not cool doing that in a mean and condescending way. Luckily everyone at Responsive is generally pretty friendly, so this is rarely an issue. 🙂

Task Loggers

When you write a task description or comment, be a human, think about the person that will be reading it. If that person is not another developer, don't use all your tech jargon. If you're writing things and the other person doesn't understand you, are you communicating?

Always aim for simplicity and clarity.

Testing

I'll be first to admit that testing is not always the most fun thing to do. No one makes hacker movies about testing; it just isn't that sexy.

There are probably as many opinions about the different types of testing as there are developers with right hands, so here's mine for UI testing:

  • Not all types of testing are useful
  • TDD does not work well for pure presentational components. It is much easier to add tests after the fact for this.
  • Your tests should reflect how a user would interact with the UI as closely as possible - tweet inspiration
  • Sometimes tests are not worth the time investment
  • Sometimes tests will save your bacon
  • Unit tests are beneficial for developers and not for stakeholders - stakeholders should not be concerned with code coverage. Developers should write enough unit tests for them to feel confident, rolling out quality code.
  • Unit tests work best for pure logic-based functions
  • There is a much higher ROI for end-to-end and integration tests than for unit tests. It is better to spend time setting up automated tests to cover the critical journeys in your application than to chase 100% test coverage for your unit tests.
  • Tests should run as part of your CI process so that adding code to your main branches requires passing tests.
  • Tests should be first-class code in your app and updated alongside the rest of your app code.

We now know that we want to put quality first, but how do we practically do that? We touched on many useful tools, but I want to take the rest of this post to chat about one particular tool that puts quality front and centre: Cypress.io

Cypress

Cypress.io is an open-source, front-end testing tool, built for the modern web.

Why Cypress?

Cypress is most often compared to Selenium, but it is fundamentally different based on its architecture. Some of you, I know, have experience with tools like Chimp and Protractor that uses Selenium under the hood. Chimp uses WebDriver, which is Selenium.

I'll admit that writing BDD tests on CHEP was probably my least favourite part while I was on the project. Don't get me wrong, the idea of automated UI tests excited me just as much as any other dev-nerd, but I sat most of the time debugging flakey tests. It just never seemed to do what I wanted. This led to zero confidence in the tool. What is the point in doing it if it doesn't give us confidence in the app's quality?

Where Selenium and other similar tools interact with your application by sending commands from outside the browser, Cypress takes an entirely different approach and works directly within your browser.

Why is this great?

Cypress can do things that Selenium simply can't do. Things like mocking out network requests, setting items in local storage, and directly interacting with your app's state. Selenium can only make educated guesses about your app's state; Cypress knows intricately what is going on in your app.

No more flake! No more pain!

Getting started

Getting up and running with Cypress is extremely easy, so I won't be covering it in this post. Install it from NPM and then to run it. Their documentation is excellent - I encourage you to take a look.

The first time you run the cypress open command, it will install Cypress itself and then open the interface. This UI is an Electron app, and like everything else about Cypress, it is 100% written in JavaScript.

You can run Cypress with the UI Electron app or headless using the cypress run command. The headless option is useful for integrating Cypress into your CI pipeline.

Killer Features

  • Time travel
    • Cypress takes screenshots of every step in your tests so you can quickly go back and see how your app behaved at a certain point
  • Realtime reloads
    • Cypress is meant to run while you develop your application and will reload the particular tests as you code. Truly quality-first.
  • Debuggability
    • Since Cypress runs inside the same browser context as your app, you can easily debug it with familiar tools. Drop a debugger straight in the code or console.log to your heart's content. Should I mention that since Selenium uses the same debug protocol as the browser developer tools, it is impossible to open the dev tools while it is running? It is crazy how we have been fine with this for so long.
    • On top of that, Cypress gives you meaningful error messages.
  • Automatic waiting
    • If you’ve ever written these UI end-to-end tests using a Selenium-based framework, you'll be keenly aware of the fact that you have to wait for everything. I'm not talking about waiting because Selenium is slow (that too!), I'm talking about waiting for every asynchronous action you perform. Cypress has waiting built-in, so you rarely have to think about it again.
  • Spies, stubs, and clocks
    • All the mocking functionality you're used to in regular unit tests, available for e2e tests
  • Network traffic control
    • Don't wait for an API to exist before developing your front-end. Mock out your API endpoints and start crafting your UI.
  • Consistent results
    • Because of the auto-waiting and the fact that Cypress runs inside your browser, tests are not flakey, unless your app is.
  • Screenshots and videos
    • View screenshots of failures or watch a video of your full suite run headlessly on your CI server.

I've been relatively harsh towards Selenium up until now. It is only fair to say that Selenium is a decent product that served its time well. In the past, before the era of JavaScript frameworks, web apps were a lot simpler. Every user action almost always resulted in a page refresh, which means there was little to no state that needed to be handled by the client. Today's apps are incredibly complex, and we need new tools to help us make sense of it all. Cypress is such a tool.

Some links:

Discuss on Twitter