Slide 1

Slide 1 text

Making SoundCloud’s µ-services safe to deploy Move Fast and Consumer-Driven- Contract-Test Things microXchg Berlin 2017

Slide 2

Slide 2 text

1.5 years @ SoundCloud Discovery Cluster Backend Engineer @alonpeer

Slide 3

Slide 3 text

About SoundCloud a cloud full of sounds 135M tracks, 175M listeners, 12 hours/minute 300+ services / 50+ teams

Slide 4

Slide 4 text

Agenda ● Testing, monolith style ● My first steps @ SoundCloud ● Introducing contract tests ● Pactifying our services ● Caveats ● QA Q&A

Slide 5

Slide 5 text

Testing, monolith style

Slide 6

Slide 6 text

● One service, one client ● API changes are easy ● One client team to sync with for migrations ● API deprecation is easy Testing, monolith style The good ol’ days

Slide 7

Slide 7 text

● Different requirements per client => Code complexity increases ● Harder to deploy without breaking at least one client Testing, monolith style Mo clients, mo problems

Slide 8

Slide 8 text

● More manual QA expensive, slow, prone to human error, doesn’t scale ● More end-to-end tests Maintenance nightmare, flaky, slow, creates bottlenecks Testing, monolith style Mo clients, mo problems

Slide 9

Slide 9 text

My first steps @ SoundCloud

Slide 10

Slide 10 text

My first steps @ SoundCloud Whoa, micro-services

Slide 11

Slide 11 text

My first steps @ SoundCloud Testing strategy E2E Unit Integration

Slide 12

Slide 12 text

My first steps @ SoundCloud Digging into post-mortems © ofsmallthings

Slide 13

Slide 13 text

My first steps @ SoundCloud “A lack of trust in the acceptance tests caused us to largely ignore the warnings they generated.” “We couldn’t figure out the impact of the broken acceptance tests, and assumed the only problem was the tests themselves, rather than the underlying code.” “The commit went through as there weren't any tests for the serialisation from the client to the backend.” Digging into post-mortems © ofsmallthings

Slide 14

Slide 14 text

Introducing contract tests

Slide 15

Slide 15 text

Introducing contract tests My daily routine © LockeSteerpike

Slide 16

Slide 16 text

Introducing contract tests My daily routine © rickandmorty.com

Slide 17

Slide 17 text

Unit tests are not enough Introducing contract tests “#cake” >> { result = call(“cake”) assert(result == “lie”) } GET “/cake”: return “lie”

Slide 18

Slide 18 text

Unit tests are not enough Introducing contract tests GET “/cake”: return “truth” “#cake” >> { result = call(“cake”) assert(result == “lie”) }

Slide 19

Slide 19 text

Unit tests are not enough Introducing contract tests import JsonLibFoo GET “/cake”: return toJson(current_state) // { “current_state” : “lie” } import JsonLibFoo “#cake” >> { result = fromJson(call(“cake”)) assert(result == “lie”) }

Slide 20

Slide 20 text

Unit tests are not enough Introducing contract tests import JsonLibBar GET “/cake”: return toJson(current_state) // { “currentState” : “lie” } import JsonLibBar “#cake” >> { result = fromJson(call(“cake”)) assert(result == “lie”) }

Slide 21

Slide 21 text

Introducing contract tests How this works? Consumer Provider End-to-End Test

Slide 22

Slide 22 text

Introducing contract tests How this works? Consumer Provider Unit Test Unit Test

Slide 23

Slide 23 text

Introducing contract tests How this works? Consumer Provider Unit Test Unit Test

Slide 24

Slide 24 text

Introducing contract tests How this works? ➢ Requesting “/cake”. ➢ Expecting a JSON response with a key “current_state”. ➢ Asserting deserialization of the response succeeds. import JsonLibFoo GET “/cake”: return toJson(current_state) // { “current_state” : “lie” } Consumer Provider

Slide 25

Slide 25 text

Pactifying our services http://pact.io

Slide 26

Slide 26 text

What’s Pact? Pactifying our services ● A family of frameworks designed for contract testing. ● Supports JSON over HTTP. ● Impl. for Java, Scala, Go, Ruby, .Net, Swift, JS etc. ● Tests run locally, no external dependencies.

Slide 27

Slide 27 text

What’s Pact? Pactifying our services Step 1: Define consumer expectations © pact.io

Slide 28

Slide 28 text

What’s Pact? Pactifying our services Step 2: Verify expectations on provider © pact.io

Slide 29

Slide 29 text

Consumer: client code

Slide 30

Slide 30 text

Consumer: client code

Slide 31

Slide 31 text

Consumer: client code

Slide 32

Slide 32 text

Consumer: client code

Slide 33

Slide 33 text

Consumer: client code

Slide 34

Slide 34 text

Consumer: client code

Slide 35

Slide 35 text

Consumer: client unit test

Slide 36

Slide 36 text

Consumer: client unit test

Slide 37

Slide 37 text

Consumer: client unit test

Slide 38

Slide 38 text

Consumer: client unit test

Slide 39

Slide 39 text

Consumer: client contract unit test

Slide 40

Slide 40 text

Consumer: client contract unit test

Slide 41

Slide 41 text

Consumer: client contract unit test

Slide 42

Slide 42 text

Consumer: client contract unit test

Slide 43

Slide 43 text

Consumer: client contract unit test

Slide 44

Slide 44 text

Consumer: client contract unit test

Slide 45

Slide 45 text

Consumer: client contract unit test

Slide 46

Slide 46 text

Consumer: client contract unit test

Slide 47

Slide 47 text

Consumer: my-consumer-likes.json

Slide 48

Slide 48 text

Consumer: my-consumer-likes.json

Slide 49

Slide 49 text

Consumer: my-consumer-likes.json

Slide 50

Slide 50 text

Consumer: my-consumer-likes.json

Slide 51

Slide 51 text

Consumer: my-consumer-likes.json

Slide 52

Slide 52 text

Consumer: my-consumer-likes.json

Slide 53

Slide 53 text

Consumer: my-consumer-likes.json

Slide 54

Slide 54 text

Pactifying our services Provider side verification ● Collect pact files from consumers and verify them all. ● Keep your provider environment isolated. ○ Dockerized databases. ○ Test doubles for upstream services.

Slide 55

Slide 55 text

Pactifying our services Provider states

Slide 56

Slide 56 text

Pactifying our services Provider states Likes App Likes DB

Slide 57

Slide 57 text

Pactifying our services Provider states Set state: “User 1000 has liked 2 tracks” Likes App Likes DB

Slide 58

Slide 58 text

Pactifying our services Provider states INSERT likes Set state: “User 1000 has liked 2 tracks” Likes App Likes DB

Slide 59

Slide 59 text

Pactifying our services Provider states INSERT likes Set state: “User 1000 has liked 2 tracks” HTTP Request: GET /likes/1000 Likes App Likes DB

Slide 60

Slide 60 text

Pactifying our services Provider states HTTP Request: GET /likes/1000 HTTP Response: 200 OK INSERT likes Set state: “User 1000 has liked 2 tracks” Likes App Likes DB

Slide 61

Slide 61 text

Pactifying our services Provider states Likes App Likes DB HTTP Response: 200 OK INSERT likes Set state: “User 1000 has liked 2 tracks” HTTP Request: GET /likes/2000 HTTP Response: 404 Not Found Set state: “User 2000 has liked no tracks” HTTP Request: GET /likes/1000 DELETE likes

Slide 62

Slide 62 text

Pactifying our services Pact broker ● Share pact files between projects. ● API for pact frameworks to fetch all pacts by provider. ● Support for versioning and tagging. ● Useful UI and visualization of the network.

Slide 63

Slide 63 text

Pactifying our services Pact broker © pact broker

Slide 64

Slide 64 text

Pactifying our services Pact broker © pact broker

Slide 65

Slide 65 text

Pactifying our services Pact broker © pact broker

Slide 66

Slide 66 text

Pactifying our services Our CI pipeline Push new change Generate consumer contracts Deploy Upload pacts & tag with ‘prod’ Consumer pipeline

Slide 67

Slide 67 text

Pactifying our services Our CI pipeline Push new change Deploy Upload pacts & tag with ‘prod’ Consumer pipeline Push new change Verify all ‘prod’-tagged pacts Deploy Provider pipeline Generate consumer contracts

Slide 68

Slide 68 text

● Communication is key. Caveats

Slide 69

Slide 69 text

● Communication is key. ● New moving parts. Caveats

Slide 70

Slide 70 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. Caveats

Slide 71

Slide 71 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. ● Frameworks are WIP. Caveats

Slide 72

Slide 72 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. ● Frameworks are WIP. ● Postel's law (the robustness principle). Caveats

Slide 73

Slide 73 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. ● Frameworks are WIP. ● Postel's law (the robustness principle). ● Provider side setup is time consuming. Caveats

Slide 74

Slide 74 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. ● Frameworks are WIP. ● Postel's law (the robustness principle). ● Provider side setup is time consuming. ● Automating consumer-triggered provider verification. Caveats

Slide 75

Slide 75 text

● Communication is key. ● New moving parts. ● Learning curve per pact framework. ● Frameworks are WIP. ● Postel's law (the robustness principle). ● Provider side setup is time consuming. ● Automating consumer-triggered provider verification. ● Adoption is essential. Caveats

Slide 76

Slide 76 text

Recap

Slide 77

Slide 77 text

Recap Write contract tests Consum ers

Slide 78

Slide 78 text

Recap Write contract tests Generate pact files Consum ers

Slide 79

Slide 79 text

Recap Write contract tests Generate pact files Publish to broker Consum ers

Slide 80

Slide 80 text

Recap Write contract tests Generate pact files Publish to broker Verify pacts Consum ers Providers

Slide 81

Slide 81 text

Recap Write contract tests Generate pact files Publish to broker Verify pacts Consum ers Providers

Slide 82

Slide 82 text

QA Q&A @alonpeer