QA Without QA

QA Without QA

Everybody knows how the fast growth the tech industry has been going through in the past few years. That kind of explosive growth creates problems and opportunities for tech companies. One of those dilemmas is Quality Assurance (QA), a common topic of conversation in a lot of engineers groups.

The standard QA process

The story is usually straightforward. A tech company is growing quickly. As the pressure on product teams to deliver mounts, quality declines. Bugs and outages drive customers away, management is pressured to find a way out of it. Most companies in this position scramble a quality initiative and start hiring what are conventionally called “quality assurance engineers”. Boom – the traditional QA process is in place.

But what does a QA engineer do? People in this role usually are responsible for helping the product development organization prevent regressions and keep everything in its right place. They wait for product teams to deploy relatively big releases to a test environment shared with other teams. Sometimes they manually test those releases, but often they spend their time making those tests automatic (great QA engineers are masters at this). If they can’t find major issues, that release is approved to see the light of day. But usually they find a bunch of errors, that are reported back to the product team, and the engineers try to fix the issues before starting the process again as many times as it takes to get it live.

There’s nothing inherently wrong with this. The traditional QA process works and many large, innovative tech companies use it successfully every day. But there are tradeoffs.

First, as the company grows, it becomes harder and slower to track those bugs. Most importantly, delegating QA to non-product teams that process reduces the incentives engineers have to ensure the quality of their work. That leads, over time, to a lack of ownership over the product. And, without ownership, it’s hard to innovate.

Origin’s choice

That’s why we decided to take a different route when we started Origin. Instead of putting quality at the end of the product development process, we think about quality upfront. Our product teams own our product. They spend a large chunk of their time in discovery, talking to customers, refining tasks and over-communicating with stakeholders. That process creates an insane level of ownership.

That is potentialized by hiring engineers that want the autonomy to do much more than just writing code. By involving them in the discovery process, we incentivize everyone to use the product and to spread bad news when fires happen so those can be put out quickly. Besides being less bureaucratic, that choice makes our product teams nimbler, keeps things simple and allows Origin to deliver a quality, innovative product to our customers.

Tools and tips

To prevent our choice from overwhelming our product teams, every single engineer has to be responsible for maintaining a high quality level and to build tools that will make it easy for the rest of the team to do so. Over time, that has led Origin to have a large unit and end-to-end test suites that help our teams move quickly and safely.

Unit tests

Unit tests are the tests of the minor part of code. They take care of validating only the function and nothing more. Each programming language has a number of frameworks to create unit tests. The most commonly used in the Javascript community is Jest, which works with many frameworks like Babel, TypeScript, Node, React, Angular, and Vue. We use React and Typescript at Origin and Jest is fundamental to allow our engineers to build new features feeling safe that they will not break another part of the application.

Function

const foo = (bar) => {
  if (bar) {
     return 'I am inside the conditional'
  }

  return 'I am outside of the conditional'
}

Test

describe('Functions | foo', () => {
   it('should return the string "I am inside the conditional"', () => {
	  const result = foo(true);
	  expect(result).toBe('I am inside the conditional');
   });

   it('should return the string "I am outside of the conditional"', () => {
      const result = foo(false);
	  expect(result).toBe('I am outside of the conditional');
   });
});

End to end tests

To prevent regressions in the the application as a whole, Origin has built a large suite of tests that cover specific flows in our application, like authentication. There are amazing tools out there to create end to end tests. Some are no-code, like Reflect or Ghost Inspector, while others require coding skill to be built and maintained. Good examples of the latter are open-source tools like Testcafe, and Cypress.

Test

describe('My First Test', () => {
  it('Gets, types and asserts', () => {
    cy.visit('<https://example.cypress.io>')

    cy.contains('type').click()

    cy.url().should('include', '/commands/actions')

    cy.get('.action-email')
      .type('fake@email.com')
      .should('have.value', 'fake@email.com')
  })
})

Result

Cypress result screen

Start or keep applying

There’s nothing wrong with separating the product and the QA functions, but we believe there are strong upsides to this cultural change. Involve engineers in the whole product development process, spread the bad news, keep things simple, find the correct tools, and appreciate the transformation that will happen to the company.

Show Comments