Slide 1

Slide 1 text

! H ? t A ! A ef ct ri g to y

Slide 2

Slide 2 text

I'm G pa S ks in al ! S S e E r @ S r ! @G s a ! o l .g 2

Slide 3

Slide 3 text

W at’s ef ct ri g? @GopalAkshintala overfullstack.ga 3

Slide 4

Slide 4 text

W at’s ef ct ri g? @GopalAkshintala overfullstack.ga 4

Slide 5

Slide 5 text

A a ev, I ee t R fa to , t m ke od ba e ef ct ra le @GopalAkshintala overfullstack.ga 5

Slide 6

Slide 6 text

@GopalAkshintala overfullstack.ga 6

Slide 7

Slide 7 text

H w o it h ou S or f r un in ? @GopalAkshintala overfullstack.ga 7

Slide 8

Slide 8 text

M th ds O st cl s M tr cs @GopalAkshintala overfullstack.ga 8

Slide 9

Slide 9 text

M th ds ! Human Readability ! Component Isolation @GopalAkshintala overfullstack.ga 9

Slide 10

Slide 10 text

O st cl s ! Exceptions ! Mutability @GopalAkshintala overfullstack.ga 10

Slide 11

Slide 11 text

M tr cs ! Cognitive Complexity ! Testability @GopalAkshintala overfullstack.ga 11

Slide 12

Slide 12 text

Methods Metrics ! Human Readability " Cognitive Complexity ! Component Isolation " Testability Obstacles ! Exceptions ! Mutability @GopalAkshintala overfullstack.ga 12

Slide 13

Slide 13 text

L t's ta t it o r O st cl s @GopalAkshintala overfullstack.ga 13

Slide 14

Slide 14 text

O st cl - 1 E ce ti ns @GopalAkshintala overfullstack.ga 14

Slide 15

Slide 15 text

E ce ti ns re he os A us d l ng ag f at re Catch or Rethrow anything try { !!" } catch(NullPointerException | IllegalArgumentException | IndexOutOfBoundsException e) { e.printStackTrace(); } try { !!" } catch(Exception e) { throw e; } @GopalAkshintala overfullstack.ga 15

Slide 16

Slide 16 text

O st cl - 1 - E ce ti ns E ce ti ns re or ho es ! @GopalAkshintala overfullstack.ga 16

Slide 17

Slide 17 text

I Es @GopalAkshintala overfullstack.ga 17

Slide 18

Slide 18 text

E ce ti ns re or ho es Tightly coupled to not just the caller, but the entire call hierarchy void verifyUserAccess(String userId) throws NoAccessException { if (!hasAccess1(userId)) { throw new NoAccessException(ACCESS_1); } if (!hasAccess2(userId)) { throw new NoAccessException(ACCESS_2); } if (!hasAccess3(userId)) { throw new NoAccessException(ACCESS_3); } } @GopalAkshintala overfullstack.ga 18

Slide 19

Slide 19 text

I’m so at d ! Optional verifyUserAccess(String userId) { if (!hasAccess1(userId)) { return Optional.of(new NoAccessException(ACCESS_1)); } if (!hasAccess2(userId)) { return Optional.of(new NoAccessException(ACCESS_2)); } if (!hasAccess3(userId)) { return Optional.of(new NoAccessException(ACCESS_3)); } return Optional.empty(); } @GopalAkshintala overfullstack.ga 19

Slide 20

Slide 20 text

R pl ce xc pt on w th DT ‣ Optional - some OR none ‣ Tuple (VAVR) - 1 WITH/AND 2 ‣ Either (VAVR) - left OR right ‣ With the advent of Pattern Matching in Java 16+, these blend more naturally! @GopalAkshintala overfullstack.ga 20

Slide 21

Slide 21 text

E ce ti ns is se t r tu n ul ip e D ta yp s int parse(String s) { if (s.matches("-?[0-9]+")) { return Integer.parseInt(s); } throw new NumberFormatException("Not a valid integer"); } @GopalAkshintala overfullstack.ga 21

Slide 22

Slide 22 text

R pl ce xc pt on it E th r !" Before int parse(String s) { if (s.matches("-?[0-9]+")) { return Integer.parseInt(s); } throw new NumberFormatException("Not a valid integer"); } !" After Either parse(String s) { if (s.matches("-?[0-9]+")) { return Either.right(Integer.parseInt(s)); } return Either.left(new NumberFormatException("Not a valid integer")); } @GopalAkshintala overfullstack.ga 22

Slide 23

Slide 23 text

O st cl - 1 - E ce ti ns T ro a ay xc pt on @GopalAkshintala overfullstack.ga 23

Slide 24

Slide 24 text

O st cl - 2 M ta le ta e @GopalAkshintala overfullstack.ga 24

Slide 25

Slide 25 text

S ar d ut bl s at @GopalAkshintala overfullstack.ga 25

Slide 26

Slide 26 text

S ar d ut bl s at o a S ar d od ba e @GopalAkshintala overfullstack.ga 26

Slide 27

Slide 27 text

O st cl - 2 - M ta il ty M ta le bj ct a I pu P ra s ou le om on nt @GopalAkshintala overfullstack.ga 27

Slide 28

Slide 28 text

static int sum(List nums) { int result = 0; for (int num : nums) { result += num; } return result; } @GopalAkshintala overfullstack.ga 28

Slide 29

Slide 29 text

static int sumAbsolute(List nums) { for (int i = 0; i < nums.size(); i!") { nums.set(i, Math.abs(nums.get(i))); } return sum(nums); !# DRY } @GopalAkshintala overfullstack.ga 29

Slide 30

Slide 30 text

static void client() { var nums = Arrays.asList(-2, 5, -6); System.out.println(sumAbsolute(nums)); } ! GA release! "# @GopalAkshintala overfullstack.ga 30

Slide 31

Slide 31 text

static void client() { var nums = Arrays.asList(-2, 5, -6); System.out.println(sumAbsolute(nums)); System.out.println(sum(nums)); !" 13 } @GopalAkshintala overfullstack.ga 31

Slide 32

Slide 32 text

static int sumAbsolute(List nums) { for (int i = 0; i < nums.size(); i!") { nums.set(i, Math.abs(nums.get(i))); !# Latent Bug } return sum(nums); !# DRY } static void client() { List nums = Arrays.asList(-2, 5, -6); System.out.println(sumAbsolute(nums)); System.out.println(sum(nums)); !# } @GopalAkshintala overfullstack.ga 32

Slide 33

Slide 33 text

O st cl - 2 - M ta il ty M ta le bj ct a i pu p ra s s nh ly f r so at on @GopalAkshintala overfullstack.ga 33

Slide 34

Slide 34 text

O st cl - 2 - M ta il ty H w bo t ut bl O je ts a et rn yp s? @GopalAkshintala overfullstack.ga 34

Slide 35

Slide 35 text

M ta le bj ct a R tu n yp s Date getEggLayingDate(int eggId) { !" heavy operation return queryEggLayingDateFromDB(eggId); } @GopalAkshintala overfullstack.ga 35

Slide 36

Slide 36 text

!" Dependent component - 1 boolean isLaidInFirstHalf(int eggId) { var layingDate = getEggLayingDate(eggId); if (layingDate.getDate() < 15) { return true; } return false; } !" Dependent component - 2 int calculateEggAge(int eggId, Date today) { return today.getDate() - getEggLayingDate(eggId).getDate(); } @GopalAkshintala overfullstack.ga 36

Slide 37

Slide 37 text

O e ay! !" Dependent component - 1 boolean isLaidInFirstHalf(int eggId) { var layingDate = getEggLayingDate(eggId); if (layingDate.getDate() < 15) { !" It's just logging, let's reuse the same Date obj for month and year layingDate.setDate(15); logger.info("This egg was laid before: " + layingDate); return true; } return false; } @GopalAkshintala overfullstack.ga 37

Slide 38

Slide 38 text

A ot er ay! static final Map eggLayingDateCacheById = new HashMap!"(); !# Cache Date getEggLayingDate(int eggId) { return eggLayingDateCacheById .computeIfAbsent(eggId, this!$queryEggLayingDateFromDB); } @GopalAkshintala overfullstack.ga 38

Slide 39

Slide 39 text

A ot er ay! !" Dependent component - 1 boolean isLaidInFirstHalf1(int eggId) { var layingDate = getEggLayingDate(eggId); if (layingDate.getDate() < 15) { !" It's just logging, let's reuse the same Date obj for month and year layingDate.setDate(15); !" Mutation ! logger.info("This egg was laid before: " + layingDate); return true; } return false; } !" Dependent component - 2 long calculateEggAge(int eggId, Date today) { return today.getDate() - getEggLayingDate(eggId).getDate(); !" What did I do? } @GopalAkshintala overfullstack.ga 39

Slide 40

Slide 40 text

O st cl - 2 - M ta il ty T at’s ua tu E ta gl me t ⚛ @GopalAkshintala overfullstack.ga 40

Slide 41

Slide 41 text

P in er E er wh re! @GopalAkshintala overfullstack.ga 41

Slide 42

Slide 42 text

O st cl - 2 - M ta il ty B t hy s ut bi it p ed mi an i J va od ? @GopalAkshintala overfullstack.ga 42

Slide 43

Slide 43 text

P we o D fa lt M ta il ty s he ef ul m de n av @GopalAkshintala overfullstack.ga 43

Slide 44

Slide 44 text

O st cl - 2 - M ta il ty M ke mm ta il ty ou D fa lt @GopalAkshintala overfullstack.ga 44

Slide 45

Slide 45 text

I t ke D sc pl ne o ea t e ef ul 1 1 Designed by rawpixel.com / Freepik @GopalAkshintala overfullstack.ga 45

Slide 46

Slide 46 text

S me ui k in ! ‣ Make a habit to use final before var and function params. ‣ Follow Immutable strategy for POJOs from Oracle’s Documentation ‣ (Pre Java 16) Auto-generate Immutable version of your POJO using: ‣ Lombok (More Magic, Less Effort) ‣ Google Auto (Less Magic, More Effort) ‣ Immutables (Less Magic, More Effort) ‣ Use Record types from Java 16 @GopalAkshintala overfullstack.ga 46

Slide 47

Slide 47 text

O st cl - 2 - M ta il ty W at o nt -I mu ab es s y? @GopalAkshintala overfullstack.ga 47

Slide 48

Slide 48 text

O st cl - 2 - M ta il ty - A ti-i mu ab es I n't mm ta il ty nl f r ul i-t re di g? @GopalAkshintala overfullstack.ga 48

Slide 49

Slide 49 text

I mu ab li y sn't nl f r C nc rr nc @GopalAkshintala overfullstack.ga 49

Slide 50

Slide 50 text

O st cl - 2 - M ta il ty - A ti-i mu ab es I mu ab e bj ct d es ’t t y mp ra iv s yl ! @GopalAkshintala overfullstack.ga 50

Slide 51

Slide 51 text

M ta il ty & I pe at vi y re ri nd ! @GopalAkshintala overfullstack.ga 51

Slide 52

Slide 52 text

M ta il ty ik s o e it I pe at vi y void mutableFn() { var mutableList = Arrays.asList("a", "b", "c"); mutateList(mutableList); } List mutateList(List list) { for (var i = 0; i < list.size(); i!") { list.set(i, list.get(i).toUpperCase()); } return list; } @GopalAkshintala overfullstack.ga 52

Slide 53

Slide 53 text

I pe at ve ut ti ns ep ac d y D cl ra iv T an fo ma io s void immutableFn() { final var immutableList = List.of("a", "b", "c"); transformList(immutableList); } List transformList(final List list) { !" `toList()` is new in Java 16 to collect Stream into UnmodifiableList. return list.stream().map(String!#toUpperCase).toList(); } @GopalAkshintala overfullstack.ga 53

Slide 54

Slide 54 text

I mu ab li y & T an fo ma io a e C up e! N C oi e! @GopalAkshintala overfullstack.ga 54

Slide 55

Slide 55 text

I mu ab li y or es ra sf rm ti n void immutableFn() { final var immutableList = List.of("a", "b", "c"); !" ! Throws UnsupportedOperationException mutateList(immutableList); } @GopalAkshintala overfullstack.ga 55

Slide 56

Slide 56 text

O st cl - 2 - M ta il ty - A ti-i mu ab es D es 't mm ta il ty e t er ? @GopalAkshintala overfullstack.ga 56

Slide 57

Slide 57 text

I mu ab li y nd er or an e ctrl-c + ctrl-v from Oracle’s Documentation ‣ The impact of object creation is often overestimated. ‣ It can be offset by decreased overhead due to garbage collection. @GopalAkshintala overfullstack.ga 57

Slide 58

Slide 58 text

J va’s mb ac ng mm ta il ty, s ow y ‣ Most used Data type in any Java application? ‣ String - No coincidence that it’s Immutable. ‣ Java 8 replaced Date with immutable LocalDate. ‣ Java 11 introduced Immutable Collections. ‣ Java 16 introduced Record types, toList on Stream for UnmodifiableList ‣ And many moreG @GopalAkshintala overfullstack.ga 58

Slide 59

Slide 59 text

H ll R al or d @GopalAkshintala overfullstack.ga 59

Slide 60

Slide 60 text

@GopalAkshintala overfullstack.ga 60

Slide 61

Slide 61 text

@GopalAkshintala overfullstack.ga 61

Slide 62

Slide 62 text

@GopalAkshintala overfullstack.ga 62

Slide 63

Slide 63 text

H w as he od ? @GopalAkshintala overfullstack.ga 63

Slide 64

Slide 64 text

H w as he od ? ! void filterDuplicates(Map failureMap, List eggsFromRequest) {!!"} void validate(Map failureMap, List nonDuplicateEggs) {!!"} List toEntityObjs(List validEggs) {!!"} void bulkInsertIntoDB(List eggEntityObjs) throws DMLOperationException {!!"} @GopalAkshintala overfullstack.ga 64

Slide 65

Slide 65 text

P rt ti n et ee S ep filterDuplicates(failureMap, eggsFromRequest); !!" !!" !# Partition `eggsFromRequest` using `failureMap` !# into `duplicateEggs`, `nonDuplicateEggs` !!" !!" validate(failureMap, nonDuplicateEggs); @GopalAkshintala overfullstack.ga 65

Slide 66

Slide 66 text

P rt ti n et ee S ep filterDuplicates(failureMap, eggsFromRequest); !" Partition var nonDuplicateEggs = new ArrayList(); for (Egg egg : eggsFromRequest) { if (!failureMap.containsKey(egg.getId())) { nonDuplicateEggs.add(egg); } } validate(failureMap, nonDuplicateEggs); @GopalAkshintala overfullstack.ga 66

Slide 67

Slide 67 text

H w as t? ! filterDuplicates(failureMap, eggsFromRequest); !!" !# Partition !!" validate(failureMap, nonDuplicateEggs); !!" !# Partition !!" var eggEntityObjs = toEntityObjs(validEggs); try { bulkInsertIntoDB(eggEntityObjs); } catch (DMLOperationException ex) { handlePartialFailures(failureMap, ex); } !# Prepare response from `failureMap` and db insertion results @GopalAkshintala overfullstack.ga 67

Slide 68

Slide 68 text

H w t's ef ct re i to ! @GopalAkshintala overfullstack.ga 68

Slide 69

Slide 69 text

T e ig at re hi t !" Before void filterDuplicates(Map failureMap, List eggsFromRequest) {!!#} !" After static List, ImmutableEgg!$ filterDuplicates( final List eggsFromRequest) {!!#} @GopalAkshintala overfullstack.ga 69

Slide 70

Slide 70 text

U in E th r or ar ia F il re List, ImmutableEgg>> @GopalAkshintala overfullstack.ga 70

Slide 71

Slide 71 text

P rt al ai ur s ow s re m ↡ List, ImmutableEgg>> @GopalAkshintala overfullstack.ga 71

Slide 72

Slide 72 text

V li at ng n it er !" Before void validate(Map failureMap, List nonDuplicateEggs) {!!#} !" After static Either, ImmutableEgg> validate( final Either, ImmutableEgg> egg) {!!#} @GopalAkshintala overfullstack.ga 72

Slide 73

Slide 73 text

V li at ng n it er static Either, ImmutableEgg> validate( Either, ImmutableEgg> eggToValidate) { return eggToValidate !" No validations execute if egg is Either.left .flatMap(validateAge) .flatMap(validateField1) !!# !!# ; } @GopalAkshintala overfullstack.ga 73

Slide 74

Slide 74 text

74

Slide 75

Slide 75 text

R fa to ed - N S ri gs tt ch d! !" void filterDuplicates(Map failureMap, List eggsFromRequest) {!!#} static List, ImmutableEgg!$ filterDuplicates(final List eggsFromRequest) {!!#} !" void validate(Map failureMap, List nonDuplicateEggs) {!!#} static Either, ImmutableEgg> validate(final Either, ImmutableEgg> egg) {!!#} !" List toEntityObjs(List validEggs) {!!#} static Either, EggEntity> toEntityObj(final Either, ImmutableEgg> egg) {!!#} !" Partition !" void bulkInsertIntoDB(List eggEntityObjs) throws DMLOperationException {!!#} static Either eggEntityObjs) {!!#} @GopalAkshintala overfullstack.ga 75

Slide 76

Slide 76 text

L ke M th er va io static List, ImmutableEgg!" filterDuplicates(final List eggsFromRequest) {!!#} static Either, ImmutableEgg> validate(final Either, ImmutableEgg> egg) {!!#} static Either, EggEntity> toEntityObj(final Either, ImmutableEgg> egg) {!!#} !$ Partition static Either eggEntityObjs) {!!#} @GopalAkshintala overfullstack.ga 76

Slide 77

Slide 77 text

L t’s la s me eg @GopalAkshintala overfullstack.ga 77

Slide 78

Slide 78 text

F nc io C mp si io final var partition = filterDuplicates(immutableEggsFromRequest).stream() .map(EggService!"validate) .map(EggService!"toEntityObj) .collect(Collectors.partitioningBy(Either!"isRight)); final var objsToInsert = partition.get(true).stream().map(Either!"get).toList(); final var results = bulkInsertIntoDB(objsToInsert); !# Prepare response from `partition.get(false)` and `results` @GopalAkshintala overfullstack.ga 78

Slide 79

Slide 79 text

H w re e oi g n ur oa ? L t's sk ur et ic @GopalAkshintala overfullstack.ga 79

Slide 80

Slide 80 text

M tr c - 1 C gn ti e om le it @GopalAkshintala overfullstack.ga 80

Slide 81

Slide 81 text

C gn ti el C mp ex filterDuplicates(failureMap, eggsFromRequest); !!" !# Partition !!" validate(failureMap, nonDuplicateEggs); !!" !# Partition !!" var eggEntityObjs = toEntityObjs(validEggs); try { bulkInsertIntoDB(eggEntityObjs); } catch (DMLOperationException ex) { handlePartialFailures(failureMap, ex); } !# Prepare response from `failureMap` and db insertion results @GopalAkshintala overfullstack.ga 81

Slide 82

Slide 82 text

D d e ⊗ C gn ti e om le it ? final var partition = filterDuplicates(immutableEggsFromRequest).stream() .map(EggService!"validate) .map(EggService!"toEntityObj) .collect(Collectors.partitioningBy(Either!"isRight)); final var objsToInsert = partition.get(true).stream().map(Either!"get).toList(); final var results = bulkInsertIntoDB(objsToInsert); !# Prepare response from `partition.get(false)` and `results` @GopalAkshintala overfullstack.ga 82

Slide 83

Slide 83 text

@GopalAkshintala overfullstack.ga 83

Slide 84

Slide 84 text

C mp ex ty ‣ Different types ‣ Accidental Complexity ‣ Essential complexity ‣ Cognitive/Cyclomatic complexity ‣ Different layers ‣ Unfamiliarity vs Unreadability. ‣ Strict vs Non-extensible. @GopalAkshintala overfullstack.ga 84

Slide 85

Slide 85 text

C mp ex ty er ! final var partition = filterDuplicates(immutableEggsFromRequest).stream() .map(EggService!"validate) .map(EggService!"toEntityObj) .collect(Collectors.partitioningBy(Either!"isRight)); final var objsToInsert = partition.get(true).stream().map(Either!"get).toList(); final var results = bulkInsertIntoDB(objsToInsert); !# Prepare response from `partition.get(false)` and `results` ! Essential Complexity ! Unfamiliarity ! Strictness @GopalAkshintala overfullstack.ga 85

Slide 86

Slide 86 text

! S na Qu e™ T O je ti el m as re og it ve om le it ! My previous talks, on the usage of SonarQube™: ! Java Version ! Kotlin Version @GopalAkshintala overfullstack.ga 86

Slide 87

Slide 87 text

M tr c - 2 T st bi it @GopalAkshintala overfullstack.ga 87

Slide 88

Slide 88 text

B os y ur es ab li y @GopalAkshintala overfullstack.ga 88

Slide 89

Slide 89 text

!" * EggEntity is a legacy class and cannot be instantiated with `new` void fillEntityObj(Egg egg, EggEntity eggEntity) { if (egg.field1() !# null) { eggEntity.put(Fields.field1, egg.field1()); } if (egg.field2() !# null) { eggEntity.put(Fields.field2, egg.field2()); } if (egg.field3() !# null) { eggEntity.put(Fields.field3, egg.field3()); } } @GopalAkshintala overfullstack.ga 89

Slide 90

Slide 90 text

B it le es @Test void fillEntityObjTest() { !" * `EggEntity` Object can't be instantiated in a JUnit context. final var mockEggEntity = mock(EggEntity.class); var egg = new Egg("field1Value", null, "field3Value"); fillEntityObj(egg, mockEggEntity); verify(mockEggEntity).put(Fields.field1,"field1Value"); verify(mockEggEntity, never()).put(Fields.field2, null); verify(mockEggEntity).put(Fields.field3,"field3Value"); } @GopalAkshintala overfullstack.ga 90

Slide 91

Slide 91 text

B os y ur es ab li y U it es !== T st nt rn ls @GopalAkshintala overfullstack.ga 91

Slide 92

Slide 92 text

B os y ur es ab li y U it es === E2E es f r he ni @GopalAkshintala overfullstack.ga 92

Slide 93

Slide 93 text

B os y ur es ab li y S pa at S at c ro S gn l @GopalAkshintala overfullstack.ga 93

Slide 94

Slide 94 text

S pa at S at c ro S gn l ‣ Static: 1-1 field mapping between Egg to EggEntity ‣ Signal: For each field mapping, fill non-null Egg fields into EggEntity using put. @GopalAkshintala overfullstack.ga 94

Slide 95

Slide 95 text

S pa at S at c ro S gn l !" Static static final Map filler) { fieldMapping.forEach((fieldId, valueGetter) !% { final var fieldValue = valueGetter.apply(egg); if (fieldValue !& null) { filler.accept(fieldId, fieldValue); } }); } @GopalAkshintala overfullstack.ga 95

Slide 96

Slide 96 text

T st nl t at at er @Test void fillEntityObjTest() { final var egg = ImmutableEgg.of("field1Value", null, "field3Value"); var actualPairs = new HashMap(); fillEntityObj(egg, actualPairs!"put); var expectedPairs = Map.of( Fields.field1, "field1Value", Fields.field3, "field3Value"); assertEquals(expectedPairs, actualPairs); } @GopalAkshintala overfullstack.ga 96

Slide 97

Slide 97 text

B os y ur es ab li y D @GopalAkshintala overfullstack.ga 97

Slide 98

Slide 98 text

T y ot o nj ct nt re bj ct @Autowired public EggService( !!", @Qualifier(EGG_REPO) EggRepo eggRepo, !!" ) @GopalAkshintala overfullstack.ga 98

Slide 99

Slide 99 text

I je t nl t e un ti n @Autowired public EggService( !!", !# @Qualifier(EGG_REPO) !# EggRepo eggRepo, @Qualifier(EGG_INSERTER) Function dbInserter, !!" ) @GopalAkshintala overfullstack.ga 99

Slide 100

Slide 100 text

P rt & A ap er /C ea A ch te tu e w th ut xt a nt rf ce @Autowired public EggService( !!", !# @Qualifier(EGG_REPO) !# EggRepo eggRepo, @Qualifier(EGG_INSERTER) Function dbInserter, !!" ) !# Original Config @Bean(EGG_INSERTER) public Function insert( @Qualifier(EGG_REPO) EggRepo eggRepo) { return eggRepo!$insert; } !# Test Config @Bean(EGG_INSERTER) public Function insertNoOp() { return eggEntity !% new ID(1); !#stub } @GopalAkshintala overfullstack.ga 100

Slide 101

Slide 101 text

M tr c - 2 - T st bi it T st bi it F rs T st ov ra e ol ow @GopalAkshintala overfullstack.ga 101

Slide 102

Slide 102 text

M tr c - 2 - T st bi it T st ! R fa to (L st ut ot he as ) @GopalAkshintala overfullstack.ga 102

Slide 103

Slide 103 text

@GopalAkshintala overfullstack.ga 103

Slide 104

Slide 104 text

@GopalAkshintala overfullstack.ga 104

Slide 105

Slide 105 text

@GopalAkshintala overfullstack.ga 105

Slide 106

Slide 106 text

R fa to I cr me ta ly A p es ri ed y DD @GopalAkshintala overfullstack.ga 106

Slide 107

Slide 107 text

@GopalAkshintala overfullstack.ga 107

Slide 108

Slide 108 text

! b t.l /h2a-s id s ⌨ b t.l /h2a-c de ! B og os @GopalAkshintala overfullstack.ga 108

Slide 109

Slide 109 text

@GopalAkshintala overfullstack.ga 109