Shift left

Rémi Delgatte (@rdelgatte)

Nov 7, 2019

Let’s kick open doors!

Software quality

Functional quality

Requirements compliance = it does what it is supposed to do

Structural quality

Robustness and maintainability = it works as designed

Measuring software quality

Capers Jones

Software quality in 2012: a survey of the state of the art

Few figures

  • From 1984 through 2012
  • 24 countries
  • 675 companies
  • 13,000 projects

Bugs introduction

Bugs detection

What’s wrong with late testing ?

  • Needs to be planned and managed professionally
  • Huge amount of time spent on waiting and bug fixing

Bugs fixing costs

  • Challenge of reproducing defects
  • Time and effort it takes to track down the problem

Shifting Left

“Test early, test often”

Earlier

  • Earlier testing = earlier detection
  • Earlier detection = earlier fixing
  • Earlier fixing = significant cost reduction

Unit testing

  • Minimize reliance on late testing
  • Late cycle tests = to prove functionality (not to find bugs)

Further left (into coding)

Find defects before testing begins

Shrink bug fixing cycle

Development and testing best practices (unit tests, inspections and static code analysis)

Service virtualization to enable continuous testing

Capers Jones’ take-away

Quality excellence ROI

$4 to $37 for each $1 spent

Poor quality is cheaper…

…until the end of the coding phase

Personal experience

First, waterfall mode

With the ice cream cone

Then, being agile, we test more often…

But still…

It keeps happening too late

Better development practices

*DD

Team programming (pair / mob)

Code review

Quality is not just about being bug-free

High quality benefits projects, teams & companies

Ease on-boarding (documentation, code guidelines…)

Focus efforts in new features instead of bug fixing

Shifting left even more!

Choose your poison (language)!

Dynamic typing

Static typing

Strong vs Weak

  • Implicit conversion between data types (type coercion)
  • Ways to escape the language’s type rules

Demo

Use case

I want to know my teammates’ favourite programming languages (Front and Back)

Language

{
  "name": "Java",
  "category": "Back"
}
{
  "name": "Elm",
  "category": "Front"
}

Person

{
  "name": "Rémi",
  "role": "Developer",
  "languages": [
    {
      "name": "Elm",
      "category": "Front"
    }, 
    {
      "name": "Haskell",
      "category": "Back"
    }, 
    {
      "name": "Java",
      "category": "Back"
    }
  ]
}

Case 1

Case 2

Case 3

Step 1: Javascript

(= dynamic weak typing)

export const firstLanguageForCategory = (languageType, person) => {
  if (person != null && person.languages !== undefined) {
    return person.languages
      .filter(language => language.category === languageType)
      .shift();
  }
};

Observations

12 unit tests

  • Feedback after tests executions
  • Specific code to prevent special / error use cases

Step 2: Typescript - let’s type

export const firstLanguageForCategory = (category: LanguageCategory, person: Person): Language | undefined => {
  if (person.languages !== undefined) {
    return person.languages
      .filter(language => language.category === category)
      .shift();
  }
};

Observations

6 unit tests (50% less)

Still the model can be improved to avoid special cases (ProductOwner can have languages)

Step 3: Elm - a better model?

type Person
    = Developer String (List Language)
    | ProductOwner String

Observations

6 unit tests

No hidden use cases (pattern matching)

Demo conclusion

Write less tests by trusting your compiler doing it for you!

In the end…

  • Quality is the cheapest
  • I’ll keep shifting left to shorten the feedback loop

Images and references

Again further left?

Isolate pure functions from effects

Pure function = no side effect

textLength :: Text -> Int

Tag impure functions

logMessage :: Message -> LogLevel -> IO ()

Algebraic effects

kafkaBrokerUrl :: Members '[ EnvironmentVariables, Log] r => Sem r Text