Stay DRY in Django With Decorators

Backend website development will often require a developer to repeat themselves for routine tasks such as per-request authentication. Most of the time these repetitive blocks of code can be encapsulated within a function. Doing so ensures that your code complies with the DRY principle and is easier to maintain.

One of my favorite tools for encapsulating functionality is the Python decorator. I use them in bastions, where applicable, and they’ve helped with maintainability as the application has grown.

Take this view for example:

def index(request):
  if not request.session.get("authenticated"):
    return HttpResponseForbidden()
  # ...

As you can see, we’re performing some form of session authentication to ensure that our user is logged in.

This works great for our single view, but what happens when we expand our application adding more views that require the user to be authenticated? We end up with something like this:

def index(request):
  if not request.session.get("authenticated"):
    return HttpResponseForbidden()

  # ...

def account(request):
  if not request.session.get("authenticated"):
    return HttpResponseForbidden()

  # top secret account section ...

Note: please don’t use the snippets above as an example of proper, secure authentication as it’s only meant to be an example of repetitive code

Taking this approach, our code starts to become repetitive quickly. Not to mention, modifying our authentication process would require multiple changes throughout our codebase. This is never good. In fact, it’s bad.

Luckily each Python 3 installation comes with a plethora of personal assistants that are great at very specific tasks. In this instance, our go-to helper is the decorator.

Let’s take a look at the above examples refactored to use a decorator for authentication:

def authenticate(func):
  def wrapper(*args, **kwargs):
    request = args[0]

    if not request.session.get("authenticated"):
      return HttpResponseForbidden()

    return func(*args, **kwargs)

  return wrapper

def index(request):
  # ...

def account(request):
  # top secret account section ...

Just like that, our code is much cleaner and easier to maintain. If we ever need to make a change in our authentication process, we modify the code within our decorator.

Lastly, Django comes with some of its own decorators offering common functionality out-of-the-box. Check there next time you find yourself repeating blocks of code within your views.

Update 10:45 AM 5/5/2018

This post unfortunately omits one of Python 3’s best decorator features, as pointed out by /u/boxidea on Reddit:

This should also include a mention of functools.wraps

Keeping the docstring and name can be important, especially if you are using tools that automatically generate documentation.

I couldn’t have explained it better myself. Definitely check out functools.wraps the next time you’re needing the help of a decorator. Thanks /u/boxidea!

A developer’s most common afterthought

Quality assurance has become the most common afterthought in our industry. Websites need to be built quickly and efficiently, which doesn’t leave much time for tedious tasks like testing. Convincing your managers, or possibly even convincing yourself to commit to quality assurance, can be tough. You’re confident in your team’s ability to write quality code, why would you need to invest so much time in testing it out afterwards? It looks great in staging so it should look great in production, right?

The reality is that every website, no matter how “lean,” is essentially a mess of independent technologies stitched together with good intentions. In theory, these technologies are more than capable of intermingling. In reality, there are far too many points of failure for any one developer to predict. This is where a well-established QA process comes in. You must catch mistakes and failures before your users do, especially when it comes to first impressions.

To illustrate this, I want to share a pretty damning story about the first time I demoed bastions with a potential customer.

A valuable mistake

Bastions started out as a testing tool specifically for lead generation forms on websites (contact forms, sales forms, etc.). It still services this niche in the form of its integrations and job templates, but I ended up pivoting to provide more general testing early on. My prospect, a B2B web development agency, was very interested in bastions’ core service at the time so they scheduled a demo.

I got through the presentation deck, started showing them bastions’ key features, and created a new test to demonstrate its ease of use. Everything was going smoothly, until I decided to show them a new feature that I had pushed to production the previous day. After having bragged about this new ‘run on-demand’ feature, I attempted to run the new job I had created for them. The page loaded, the timer began, and there it went.

And it went.

And it went.

Something was clearly wrong, and I was embarrassed. I tried to run the job again, assuming it was a one-off error that wouldn’t happen twice, but I was blocked by my own safeguard put in place to limit running on-demand jobs to one. This meant that the feature did not work on the front-end nor the back-end. It was official: the feature was broken, and I looked silly.

This prospect ended up passing on bastions, and I don’t really blame them. I was pitching them a QA product that had obviously not done enough QA on itself. I was embarrassed, I wanted a do-over.

Unfortunately, there are no do-overs when it comes to first impressions. No matter what I came back with, this person would always associate my product with error and laziness. I had failed their expectations and, more importantly, I had failed the product.

QA is your duty, not your burden

We should be associating a sense of responsibility with our projects. Whether it’s for work, or a labor of love, it is our duty to ensure that the project is in a presentable state. If you’ve taken a project all the way from inception to launch, you should perform the due diligence customers expect.

Reid Hoffman, founder of LinkedIn, once said,

“If you are not embarrassed by the first version of your product, you’ve launched too late.”

This is a very sound piece of advice. However, I don’t believe that Mr. Hoffman is handing out a “free pass” to launch your product without establishing a proper QA process. Believe me, even with a QA process, there will be plenty of things about your product to be embarrassed by at first. Do yourself a favor and make sure those embarrassing moments aren’t due to your basic features failing.

Establish a QA process, whether that be automated or manual, and stick to it. Don’t ignore your responsibility to test, it will come back to haunt you.

Why you should be testing your website’s forms

The case for automated form testing

Companies today rely on their web properties to generate a steady stream of revenue, whether directly (e.g., e-commerce) or indirectly (e.g., marketing websites). Most businesses use lead-aggregation forms, such as sales forms, contact forms and signup forms, to neatly funnel visitors into their digital rolodex.

Of these businesses, the majority do not have a system in place to notify them of unexpected breaks, errors, technical misconfigurations, or any of the other “whoopsies” that will occur during the lifetime of their website. When something inevitably breaks, these businesses will realize their faults slowly—long after their users—and miss out on the unknown potential revenue that vanished into the abyss while they assumed everything was working as expected.

The difference between becoming another chump whose assumptions result in lost revenue and frustrated would-be customers and establishing yourself as a diligent guardian of your online business is process.

Forms have massive potential for failure

Modern web forms have enough moving parts to make even a developer dizzy, but as one of the oldest and most understood technologies on the internet, their functionality is often taken for granted. It is this underestimation, however, that has allowed the modern sales form to become one of the biggest points of failure for online businesses today.

A typical submission process today consists of front-end code, back-end code, a web server, a database server, and a CRM. Each of these belong to different domains of our technology stack, yet we rely on all of them to function flawlessly just to receive a prospective customer’s email address.

There are too many moving parts involved in the modern sales funnel to simply assume everything is working at all times.

Acknowledging the potential for failure is the first step, but what can be done about it?

The Testing Process: A Safety Net

As humans, we are not always good at anticipating everything that could go wrong. We are not always good at safeguarding ourselves from small mistakes with large consequences. We are not always good at predicting how the dominos will fall.

If you enjoy running an online business, and would like to continue doing that in the future, you need an established QA (quality assurance) process for your website. This may seem difficult, but the majority of companies can accomplish it with one of two approaches.

Manual testing

This approach requires a team of professionals routinely performing checkups on each aspect of your website. This includes forms, broken links, front-end code errors, back-end code errors, third-party integration health-checks, and much more. This type of testing is a full-time job in our industry, and rightfully so. Great QA testers are the difference between a healthy website and a non-functioning piece of junk.

The issue with manual testing is that it is time-consuming, tedious, and costs your company valuable man hours instead of machine hours. Some aspects of your website will require manual testing, but the majority of your testing process won’t.

Automated testing

The bread-and-butter of modern QA, test automation takes tedious manual tasks and converts them into replicable processes that can be performed as often as you’d like. Because machine hours are much less expensive than man hours, this type of testing is cost-effective and efficient.

The differentiator in the cost of your business’s automated QA process comes down to one thing: will you pay an engineering firm to develop your process from scratch, or will you use a QA service with established processes? You may end up using both over time, as some testing is simply too much of an edge case to massively replicate. However, modern QA platforms provide tools that make the creation of complex test cases—even one-off edge cases—very simple.

This is where QA platforms like bastions (my own product), come into the picture. SaaS products specializing in QA processes can be a lifesaver when it comes to detecting and diagnosing unexpected problems with your website. Although this isn’t true of every product out there, many require no programming or even large amounts of technical knowledge. If you can use a mouse, and you understand how your website should work, you can establish your own testing processes with these platforms in minutes.

If you find yourself thinking something along the lines of:

“My team builds great websites with bug-free code and artisanal documentation. This couldn’t possibly happen to us!”

The good news is that you’re not the first to think this way. You also won’t be the last to change your mind once the “unthinkable” becomes the only thing your customers have on their mind while thinking about your business.

Landing the plane

If you leave this article with nothing else, remember this: your online business needs a process in place to ensure your website is functioning around the clock. Your website is an investment, the return of which depends on its functionality.

Let’s stop ignoring QA processes and start protecting our businesses from the consequences of assuming continuous functionality.