Slide 1

Slide 1 text

Selling Maintainability Tips and tricks to maintain maintainability. Tom Hombergs Principal Software Engineer

Slide 2

Slide 2 text

What have I signed up for? What is maintainability? What makes software maintainable? Impacts of (lacking) maintainability What happens if our software is maintainable (and what if not)? Tips & Tricks How to argue for spending time on maintainability? How to improve it? … on a Friday at 17:00…

Slide 3

Slide 3 text

What is maintainability?

Slide 4

Slide 4 text

Maintainable? B D F E C A

Slide 5

Slide 5 text

No circular dependencies A circular dependency creates tight coupling.

Slide 6

Slide 6 text

Maintainable? B D F E C A

Slide 7

Slide 7 text

Maintainable? B D F E C A

Slide 8

Slide 8 text

Few dependencies The fewer dependencies a component has … … the easier it is to reason about. … the less coupled it is to other components.

Slide 9

Slide 9 text

Maintainable? static void intern_rec(value *dest) { unsigned int code; tag_t tag; mlsize_t size, len, ofs_ind; value v, clos; asize_t ofs; header_t header; char cksum[16]; struct custom_operations * ops; tailcall: code = read8u(); if (code >= PREFIX_SMALL_INT) { if (code >= PREFIX_SMALL_BLOCK) { /* Small block */ tag = code & 0xF; size = (code >> 4) & 0x7; read_block: if (size == 0) { v = Atom(tag); } else { v = Val_hp(intern_dest); *dest = v; if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v; dest = (value *) (intern_dest + 1); *intern_dest = Make_header(size, tag, intern_color); intern_dest += 1 + size; for(/*nothing*/; size > 1; size--, dest++) intern_rec(dest); goto tailcall; } } else { /* Small integer */ v = Val_int(code & 0x3F); } } else { if (code >= PREFIX_SMALL_STRING) { /* Small string */ len = (code & 0x1F); read_string: size = (len + sizeof(value)) / sizeof(value); v = Val_hp(intern_dest); if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v; *intern_dest = Make_header(size, String_tag, intern_color); intern_dest += 1 + size; Field(v, size - 1) = 0; ofs_ind = Bsize_wsize(size) - 1; Byte(v, ofs_ind) = ofs_ind - len; readblock(String_val(v), len); } else { switch(code) { case CODE_INT8: v = Val_long(read8s()); break; case CODE_INT16: v = Val_long(read16s()); break; case CODE_INT32: v = Val_long(read32s()); break; case CODE_INT64: #ifdef ARCH_SIXTYFOUR v = Val_long(read64s()); break; #else intern_cleanup(); failwith("input_value: integer too large"); break; #endif case CODE_SHARED8: ofs = read8u(); read_shared: Assert (ofs > 0); Assert (ofs <= obj_counter); Assert (intern_obj_table != NULL); v = intern_obj_table[obj_counter - ofs]; break; case CODE_SHARED16: ofs = read16u(); goto read_shared; case CODE_SHARED32: ofs = read32u(); goto read_shared; case CODE_BLOCK32: header = (header_t) read32u(); tag = Tag_hd(header); size = Wosize_hd(header); goto read_block; case CODE_STRING8: len = read8u(); goto read_string; case CODE_STRING32: len = read32u(); goto read_string; case CODE_DOUBLE_LITTLE: case CODE_DOUBLE_BIG: if (sizeof(double) != 8) { intern_cleanup(); invalid_argument("input_value: non-standard floats"); } v = Val_hp(intern_dest); if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v; *intern_dest = Make_header(Double_wosize, Double_tag, intern_color); intern_dest += 1 + Double_wosize; readblock((char *) v, 8); #if ARCH_FLOAT_ENDIANNESS == 0x76543210 if (code != CODE_DOUBLE_BIG) Reverse_64(v, v); #elif ARCH_FLOAT_ENDIANNESS == 0x01234567 if (code != CODE_DOUBLE_LITTLE) Reverse_64(v, v); #else if (code == CODE_DOUBLE_LITTLE) Permute_64(v, ARCH_FLOAT_ENDIANNESS, v, 0x01234567) else Permute_64(v, ARCH_FLOAT_ENDIANNESS, v, 0x76543210); #endif break; case CODE_DOUBLE_ARRAY8_LITTLE: case CODE_DOUBLE_ARRAY8_BIG: len = read8u(); read_double_array: if (sizeof(double) != 8) { intern_cleanup(); invalid_argument("input_value: non-standard floats"); } size = len * Double_wosize; v = Val_hp(intern_dest); if (intern_obj_table != NULL) intern_obj_table[obj_counter++] = v; *intern_dest = Make_header(size, Double_array_tag, intern_color); intern_dest += 1 + size; readblock((char *) v, len * 8 #if ARCH_FLOAT_ENDIANNESS == 0x76543 if (code != CODE_DOUBLE_ARRAY8 code != CODE_DOUBLE_ARRAY3 mlsize_t i; for (i = 0; i < len; i++) Rev } #elif ARCH_FLOAT_ENDIANNESS == 0x012345 if (code != CODE_DOUBLE_ARRAY8_L code != CODE_DOUBLE_ARRAY32_ mlsize_t i; for (i = 0; i < len; i++) Rever } #else if (code == CODE_DOUBLE_ARRAY8_LIT code == CODE_DOUBLE_ARRAY32_LI mlsize_t i; for (i = 0; i < len; i++) Permute_64((value)((double *)v (value)((double *)v + } else { mlsize_t i; for (i = 0; i < len; i++) Permute_64((value)((double *)v + (value)((double *)v + } #endif break; case CODE_DOUBLE_ARRAY32_LITTLE: case CODE_DOUBLE_ARRAY32_BIG: len = read32u(); goto read_double_array; case CODE_CODEPOINTER: ofs = read32u(); readblock(cksum, 16); if (memcmp(cksum, code_checksum(), 16) != intern_cleanup(); failwith("input_value: code mismatch"); } v = (value) (code_area_start + ofs); break; case CODE_INFIXPOINTER: ofs = read32u(); intern_rec(&clos); v = clos + ofs; break; case CODE_CUSTOM: ops = find_custom_operations((char *) intern if (ops == NULL) { intern_cleanup(); failwith("input_value: unknown custom block } while (*intern_src++ != 0) /*nothing*/; /*ski size = ops->deserialize((void *) (intern_dest size = 1 + (size + sizeof(value) - 1) / sizeof v = Val_hp(intern_dest); if (intern_obj_table != NULL) intern_obj_table[ *intern_dest = Make_header(size, Custom_tag, in Custom_ops_val(v) = ops; intern_dest += 1 + size; break; default: intern_clean Few dependencies are not enough to make code maintainable.

Slide 10

Slide 10 text

Single Responsibility Principle - 
 each component has a single reason to change. Few responsibilities

Slide 11

Slide 11 text

Which is more maintainable? B D F E C A B F E C A H I J K L G D 12 dependencies 19 dependencies

Slide 12

Slide 12 text

Few dependencies (in the right direction) 
 and few responsibilities 
 per component make software maintainable. The system of components will have many dependencies and many responsibilities. Maintainability

Slide 13

Slide 13 text

Why do engineers care about maintainability?

Slide 14

Slide 14 text

Developer Joy Maintainable software is a joy to work on.

Slide 15

Slide 15 text

Why should everyone else care about maintainability?

Slide 16

Slide 16 text

Developer joy in fl uences developer productivity. Developer joy Developer productivity Maintainability

Slide 17

Slide 17 text

Developer productivity positively in fl uences developer joy. Developer joy Developer productivity Maintainability “The SPACE of Developer Productivity”: https://dl.acm.org/doi/10.1145/3454122.3454124

Slide 18

Slide 18 text

Developer joy Developer productivity Maintainability Developer retention Decision making Software architecture

Slide 19

Slide 19 text

Explaining maintainability

Slide 20

Slide 20 text

Teambuilding analogy

Slide 21

Slide 21 text

Teambuilding analogy If responsibilities in a team are unclear, everyone depends on everyone else and the team can’t work productively.

Slide 22

Slide 22 text

Broken windows analogy

Slide 23

Slide 23 text

Broken windows analogy If one window is broken, more windows will follow quickly.

Slide 24

Slide 24 text

Messy room analogy

Slide 25

Slide 25 text

Messy room analogy If a room is already messy, you have no inhibitions to make it even messier.

Slide 26

Slide 26 text

Bridge analogy

Slide 27

Slide 27 text

Bridge analogy An unmaintained bridge will collapse.

Slide 28

Slide 28 text

https://detektor.fm/digital/datenjournalismus-interaktive-karte-zeigt-marode-deutsche-bahn-bruecken

Slide 29

Slide 29 text

Evolution analogy

Slide 30

Slide 30 text

Evolution analogy A well-maintained system can evolve into the system it needs to be.

Slide 31

Slide 31 text

Cost per change over time Maintainable software Not-so-maintainable software

Slide 32

Slide 32 text

Maintaining maintainability

Slide 33

Slide 33 text

Start clean Starting clean (and then keeping it clean) prevents broken windows.

Slide 34

Slide 34 text

“A clean architecture keeps options open”

Slide 35

Slide 35 text

Record decisions Record every conscious decision that in fl uences maintainability.

Slide 36

Slide 36 text

Review decisions Review previously recorded decisions and evaluate their impact to inform future decisions.

Slide 37

Slide 37 text

Track the cost of change Observe the cost of change over time to make informed decisions.

Slide 38

Slide 38 text

Cost of change over time

Slide 39

Slide 39 text

Cost of change over time Heroic efforts to improve maintainability

Slide 40

Slide 40 text

Disaster-driven development (DDD) Heroic efforts to improve maintainability Performance reviews (and promotions)

Slide 41

Slide 41 text

Cost of change over time Decision we could have made at the start of the project This is where we could have been today … (without promotions, though) Decisions we have actually made This where we actually are today

Slide 42

Slide 42 text

Re-brand Use the term “changeability” instead of “maintainability”.

Slide 43

Slide 43 text

Developer stories As a developer I want to … so that …

Slide 44

Slide 44 text

Product owner stories As a product owner I want to … so that …

Slide 45

Slide 45 text

Budget for maintainability Every sprint, include maintainability tasks.

Slide 46

Slide 46 text

Takeaways

Slide 47

Slide 47 text

Maintainability by default When making a change, default to the more maintainable solution (unless there’s a good reason not to). Record and review decisions impacting maintainability When making a decision that impacts maintainability, record the expected impact on maintainability and compare it against the actual impact. Takeaways Maintainability creates a virtuous (or vicious) cycle Maintainability in fl uences and is in fl uenced by 
 developer joy and productivity.

Slide 48

Slide 48 text

Thank you!