Slide 1

Slide 1 text

Software Migration Strategies Symfony Live 2013 Berlin Sönke Ruempler

Slide 2

Slide 2 text

“Chief Trolling Officer” at www.jimdo.com @s0enke github.com/s0enke ruempler.eu

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

“A [software] migration strategy must ensure that the system remains fully functional during the modernization effort.” http://en.wikipedia.org/wiki/Software_modernization

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

“If you break it, will you even notice?”

Slide 7

Slide 7 text

“If noticed it, can you even fix it?”

Slide 8

Slide 8 text

DONE Thanks!

Slide 9

Slide 9 text

DONE

Slide 10

Slide 10 text

Rewrite!!!1

Slide 11

Slide 11 text

“It’s easier to write code than to read code.”

Slide 12

Slide 12 text

Rewrite?

Slide 13

Slide 13 text

“When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work.” http://www.joelonsoftware.com/articles/fog0000000069.html

Slide 14

Slide 14 text

Example!

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

3 years of downtime?

Slide 18

Slide 18 text

&& WASTE

Slide 19

Slide 19 text

Positive example?

Slide 20

Slide 20 text

https://engineering.groupon.com/2013/misc/i-tier-dismantling-the-monoliths/

Slide 21

Slide 21 text

Ok, what was the difference?

Slide 22

Slide 22 text

Partial rewrite.

Slide 23

Slide 23 text

Long-living Feature Branches for Refactorings?

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

==

Slide 26

Slide 26 text

MERGE CONFLICTS!

Slide 27

Slide 27 text

&& WASTE

Slide 28

Slide 28 text

Ok, how to do better?

Slide 29

Slide 29 text

Branch by abstraction http://paulhammant.com/blog/branch_by_abstraction.html

Slide 30

Slide 30 text

works with

Slide 31

Slide 31 text

Clean Code

Slide 32

Slide 32 text

Legacy Code

Slide 33

Slide 33 text

One master/trunk

Slide 34

Slide 34 text

Branch in the Codebase!

Slide 35

Slide 35 text

ok, step by step lets start with legacy code, makes more fun ;)

Slide 36

Slide 36 text

1. Isolate the component which needs to be replaced to in order to fulfill a business requirement

Slide 37

Slide 37 text

Migration Steps / Code examples that are: ● production-ready, ● no feature branch involved ● can be deployed as-is ● not introducing any downtime will be marked with a star.

Slide 38

Slide 38 text

Possibility A: introduce and refactor to a new indirection layer

Slide 39

Slide 39 text

Consumer Code Consumer Code Consumer Code Old component to be replaced

Slide 40

Slide 40 text

Possibility 1: introduce and refactor to a new indirection layer Consumer Code Indirection Layer Old component to be replaced Consumer Code Consumer Code

Slide 41

Slide 41 text

2. Implement the new component Consumer Code Indirection Layer Old component to be replaced New component Consumer Code Consumer Code

Slide 42

Slide 42 text

3. Add a feature toggle / flip Consumer Code Indirection Layer Old component to be replaced New component Feature Flip Consumer Code Consumer Code

Slide 43

Slide 43 text

4. Remove old component and feature flip Consumer Code Indirection Layer New component Consumer Code Consumer Code

Slide 44

Slide 44 text

4a) Maybe remove the Indirection Layer as well? Consumer Code New component Consumer Code Consumer Code

Slide 45

Slide 45 text

SUCCESS

Slide 46

Slide 46 text

Ok, so I might not need the whole indirection layer in the end. So is it worth the effort?

Slide 47

Slide 47 text

Possibility B: you may also use conditionals everywhere

Slide 48

Slide 48 text

if the codebase is hard to refactor and the new indirection layer will get obsolete eventually anyway

Slide 49

Slide 49 text

Consumer Code Consumer Code function doThis() { if (getconfig(‘migrate_legacy_component’) { // new implementation } else { // old implementation } } function doThat() { if (getconfig(‘migrate_legacy_component’) { // new implementation } else { // old implementation } } function doStuffs() { if (getconfig(‘migrate_legacy_component’) { // new implementation } else { // old implementation } } Consumer Code Feature toggles everwhere!

Slide 50

Slide 50 text

Consumer Code Consumer Code function doThis() { // new implementation } function doThat() { // new implementation } function doStuffs() { // new implementation } Consumer Code

Slide 51

Slide 51 text

Hint: Learn how to deal with spaghetti / legacy code

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Ok what if I have to deal with data migrations?

Slide 54

Slide 54 text

Consumer Code Indirection Layer Old component to be replaced New component Migration Decorator Consumer Code Consumer Code

Slide 55

Slide 55 text

Decorator pattern! class MigrationStorageProviderDecorator implements StorageProviderInterface { public function __construct( StorageProviderInterface $oldStorageProvider, StorageProviderInterface $newStorageProvider ) { $this->oldStorageProvider = $oldStorageProvider; $this->newStorageProvider = $newStorageProvider; } }

Slide 56

Slide 56 text

On-the-fly data migration class MigrationStorageProviderDecorator implements StorageProviderInterface { public function get($id) { if ($storageItem = $this->newStorageProvider->get($id)) { // found item in new storage, migration already done return $storageItem; } if ($storageItem = $this->oldStorageProvider->get($id)) { // found item in old provider, copy to new provider, then return $this->newStorageProvider->put($storageItem); return $storageItem; } throw new StorageItemNotFoundException(); } }

Slide 57

Slide 57 text

Data migration worker

Slide 58

Slide 58 text

BTW: This is Continuous Integration!

Slide 59

Slide 59 text

Protip: Always separate code cleanups from functional changes in commits!

Slide 60

Slide 60 text

Ok, so what’s up with the two initial rules?

Slide 61

Slide 61 text

“If you break it, will you even notice?”

Slide 62

Slide 62 text

MTBF vs. MTTR

Slide 63

Slide 63 text

Verify Branch by Abstraction Consumer Code Indirection Layer Old component to be replaced New component Verify by comparing old and new component results Consumer Code Consumer Code http://java.dzone.com/articles/application-pattern-verify

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Gemba Walks

Slide 66

Slide 66 text

Metrics everywhere

Slide 67

Slide 67 text

“When code runs in production, it doesn't have the protection of a clean environment like your tests do.” http://about.travis-ci.org/blog/2013-06-11-unit-testing-your-production-code/

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

“If you noticed it, can you even fix it?

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

One - some - many

Slide 73

Slide 73 text

“Defend against the impossible, because the impossible will happen.” -- Defensive Programming http://c2.com/cgi/wiki?DefensiveProgramming

Slide 74

Slide 74 text

Logs

Slide 75

Slide 75 text

www.loggly.com

Slide 76

Slide 76 text

www.logstash.org

Slide 77

Slide 77 text

www.graylog2.org

Slide 78

Slide 78 text

SUCCESS

Slide 79

Slide 79 text

Culture Requiments analysis / QA Development Production/Rollout Cont. Deployment Gemba walks High level acceptance tests (e.g. cucumber) Regression tests Isolating the component / scope of the migration Branch by Abstration Verify BBA Feature Flips Metrics (Audit)Logs One-some-many rollout Feature flags Recap

Slide 80

Slide 80 text

Thanks! Questions?