-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrss.xml
1337 lines (1326 loc) · 161 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[SPHTech Blog]]></title><description><![CDATA[Our collection of adventures, learnings and thoughts!]]></description><link>https://sphtech.github.io</link><generator>RSS for Node</generator><lastBuildDate>Sat, 14 Mar 2020 06:07:39 GMT</lastBuildDate><item><title><![CDATA[XConf Review - Economics of Software Quality]]></title><description><![CDATA[Mr Martin Fowler was one of the founding members of the Agile Manifesto, author of many books including my favourite Refactoring (1st…]]></description><link>https://sphtech.github.io/xconf-economics-of-software-quality/</link><guid isPermaLink="false">https://sphtech.github.io/xconf-economics-of-software-quality/</guid><pubDate>Mon, 29 Apr 2019 12:46:00 GMT</pubDate><content:encoded><p>Mr Martin Fowler was one of the founding members of the Agile Manifesto, author of many books including my favourite Refactoring (1st edition was written in Java, and the latest edition in Javascript), and a great speaker at many conferences. I learned a lot from watching his talks at the previous GOTO conferences. Hence, when I found out that he is coming to Singapore to speak at the first Singapore’s XConf, I immediately signed up for the tickets.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/69f138fdf3dbae16a117924acac2bfb7/5fd2a/IMG__0304.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 49.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIEAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB5jYJLUTA/wD/xAAZEAACAwEAAAAAAAAAAAAAAAACEgADECH/2gAIAQEAAQUCsRQEJxt//8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BV//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AUf/xAAaEAEAAQUAAAAAAAAAAAAAAAARAAEgISJR/9oACAEBAAY/AtVmSVO2f//EABwQAAMAAQUAAAAAAAAAAAAAAAABESEQMUFxof/aAAgBAQABPyHD7lZi+jHKYmzSD4P/2gAMAwEAAgADAAAAEBzP/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EKmH/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EIun/8QAGxABAQADAQEBAAAAAAAAAAAAAREAITFBUcH/2gAIAQEAAT8QHKs9wv01reJpUncqEjwkkOYg2liBiK6PMIaAa/c//9k='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Same title, different contents each time!"
title=""
src="/static/69f138fdf3dbae16a117924acac2bfb7/64a7b/IMG__0304.jpg"
srcset="/static/69f138fdf3dbae16a117924acac2bfb7/57443/IMG__0304.jpg 293w,
/static/69f138fdf3dbae16a117924acac2bfb7/bedd3/IMG__0304.jpg 585w,
/static/69f138fdf3dbae16a117924acac2bfb7/64a7b/IMG__0304.jpg 1170w,
/static/69f138fdf3dbae16a117924acac2bfb7/5fd2a/IMG__0304.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Same title, different contents each time!</figcaption>
</figure></p>
<p>Mr Fowler liked to use the overarching title “Software Development in the 21st Century” for his various talks, but the contents of each of those talks would vary. This time, it consisted of 2 topics: a) Economics of Software Quality and b) Practices for an Agile Codebase.</p>
<h3>Economics of Software Quality</h3>
<p>Mr Fowler started off in a rather humorous manner, mimicking his counterpart Mr Robert C. Martin (which the fraternity called as Uncle Bob), about how absolute and loud Uncle Bob would detest when bad or buggy software was allowed to be released. It is a heinous sin to commit, and no one must do that, he would say. Mr Fowler would, however, take a more measured approach to evaluate such an issue.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/11ef82391f08bed1bed9423959a565a7/5fd2a/IMG__0307.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 43.214285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAABAv/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAEveklXCh//xAAbEAACAQUAAAAAAAAAAAAAAAABAgADEhMxQv/aAAgBAQABBQJyliYRBUVZzDv/xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8Bsa//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8BjH//xAAbEAACAQUAAAAAAAAAAAAAAAAAATECECEiQf/aAAgBAQAGPwLC2FBPSu//xAAcEAEAAgEFAAAAAAAAAAAAAAABABExIVFhgaH/2gAIAQEAAT8hZWlGd40TV5JeGi0+wk57mSf/2gAMAwEAAgADAAAAEPQv/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEDAQE/ENiEGX//xAAWEQEBAQAAAAAAAAAAAAAAAAAAASH/2gAIAQIBAT8Qpp//xAAdEAACAQQDAAAAAAAAAAAAAAABEQAhMaHBQWHw/9oACAEBAAE/EGoqi13dYMzkFyxBIhFGCqElMLct+WEy9T//2Q=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Mr Fowler mimicking Uncle Bob."
title=""
src="/static/11ef82391f08bed1bed9423959a565a7/64a7b/IMG__0307.jpg"
srcset="/static/11ef82391f08bed1bed9423959a565a7/57443/IMG__0307.jpg 293w,
/static/11ef82391f08bed1bed9423959a565a7/bedd3/IMG__0307.jpg 585w,
/static/11ef82391f08bed1bed9423959a565a7/64a7b/IMG__0307.jpg 1170w,
/static/11ef82391f08bed1bed9423959a565a7/5fd2a/IMG__0307.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Mr Fowler mimicking Uncle Bob.</figcaption>
</figure></p>
<p>He reasoned that for non technical people such as business owners, they have their set of problems to address and targets to meet. When they look at software as a product, they would view the customer facing features and the underlying software quality as items that are “tradable”. Taking 3 main points — Pleasant user interface, Few defects, Good modular architecture — as example, it would be common for business owners to draw the line and separate Good modular architecture from the other two.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/2b610fb5abd2291d2f0f73bc99d065c2/5fd2a/IMG__0310.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 45.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAQKKea0bKj//xAAbEAACAgMBAAAAAAAAAAAAAAABAgADBBQxM//aAAgBAQABBQKw1mDXFYyWUN5r2f/EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGxr//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/ARn/xAAaEAEAAgMBAAAAAAAAAAAAAAABABAREjFR/9oACAEBAAY/AjUx7Hm2IA8or//EABoQAAICAwAAAAAAAAAAAAAAAAERADEQIWH/2gAIAQEAAT8hQS9i4tm8tFuLLgFcrzP/2gAMAwEAAgADAAAAENDf/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERcf/aAAgBAwEBPxC6WH//xAAXEQADAQAAAAAAAAAAAAAAAAAAARFh/9oACAECAQE/EJJk0//EAB4QAAEEAQUAAAAAAAAAAAAAAAEAESExsUFhcaHB/9oACAEBAAE/EBJEiS6b21UtbICSAsxpNUEBgHiyF2DhCzyv/9k='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Drawing the line between external and internal qualities of a software."
title=""
src="/static/2b610fb5abd2291d2f0f73bc99d065c2/64a7b/IMG__0310.jpg"
srcset="/static/2b610fb5abd2291d2f0f73bc99d065c2/57443/IMG__0310.jpg 293w,
/static/2b610fb5abd2291d2f0f73bc99d065c2/bedd3/IMG__0310.jpg 585w,
/static/2b610fb5abd2291d2f0f73bc99d065c2/64a7b/IMG__0310.jpg 1170w,
/static/2b610fb5abd2291d2f0f73bc99d065c2/5fd2a/IMG__0310.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Drawing the line between external and internal qualities of a software.</figcaption>
</figure></p>
<p>Depending on the resources available, business owners would adjust that line and give what bandwidth was left after developing the features to touch up on the Internal Quality. While this approach does help to accelerate the product’s time-to-market, this momentum would be short-lived — as short as within a few weeks. Design Stamina Hypothesis, he explained with a parabolic chart, charts out how a software with no or poor architecture design would have quick growth in cumulative functionality at the beginning, but would slow down logarithmically as the time of the project stretches.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/307f909b3021bb075076d869962df0c0/5fd2a/IMG__0312.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 44.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAF/8QAFgEBAQEAAAAAAAAAAAAAAAAAAgAB/9oADAMBAAIQAxAAAAESyHD0bKsv/8QAGxAAAQUBAQAAAAAAAAAAAAAAAQACAwQRMkH/2gAIAQEAAQUCkLCtrCIWH4efV//EABYRAQEBAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGxr//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/ARn/xAAbEAACAgMBAAAAAAAAAAAAAAABEQAhAhASUf/aAAgBAQAGPwILFezJLpVUCNDQ1//EABoQAAMBAAMAAAAAAAAAAAAAAAABETEQIYH/2gAIAQEAAT8hbr62kwlG00lnQi7hiPg//9oADAMBAAIAAwAAABBEP//EABcRAQADAAAAAAAAAAAAAAAAAAARQVH/2gAIAQMBAT8QvQx//8QAFxEBAAMAAAAAAAAAAAAAAAAAAAERUf/aAAgBAgEBPxB1Ov/EABwQAQACAgMBAAAAAAAAAAAAAAEAESExcbHBof/aAAgBAQABPxAcREaJBeXfOoObKtdhjKYgqqFR0xqp0+58j7Hyf//Z'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="The 2 chart lines for Design Stamina Hypothesis."
title=""
src="/static/307f909b3021bb075076d869962df0c0/64a7b/IMG__0312.jpg"
srcset="/static/307f909b3021bb075076d869962df0c0/57443/IMG__0312.jpg 293w,
/static/307f909b3021bb075076d869962df0c0/bedd3/IMG__0312.jpg 585w,
/static/307f909b3021bb075076d869962df0c0/64a7b/IMG__0312.jpg 1170w,
/static/307f909b3021bb075076d869962df0c0/5fd2a/IMG__0312.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">The 2 chart lines for Design Stamina Hypothesis.</figcaption>
</figure></p>
<p>Software projects which took care to set up a good design architecture may have relatively lesser features at the beginning of the project. However, because it was built with proper workflows and steady foundations, it would quickly catch up and surpass the growth rate of the former project steadily.</p>
<p>He gave another analogy of how software development can be viewed as a bank account with existing debts. When each new feature is built on to the software, one can choose either to incur more debt on top of this principal amount, or pay off a portion of it: that choice depends on the quality of the code that implemented the feature.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/cb7b1025c7567d8ecdb83d4d9573d5d7/5fd2a/IMG__0314.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 44.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAC/9oADAMBAAIQAxAAAAHKehU1YQB//8QAGRAAAwADAAAAAAAAAAAAAAAAAQITABAx/9oACAEBAAEFAmKFzKd2OPzX/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/AbGv/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BJ//EABoQAAICAwAAAAAAAAAAAAAAAAEQABIRITL/2gAIAQEABj8C5qJrFmV//8QAHBABAAIBBQAAAAAAAAAAAAAAAQBBMRARUXGh/9oACAEBAAE/IRWwsCIrgVG17MEM9NP/2gAMAwEAAgADAAAAEPMv/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8QoP/EABgRAAIDAAAAAAAAAAAAAAAAAAABESFR/9oACAECAQE/EGoh6f/EABsQAQACAgMAAAAAAAAAAAAAAAEAETGhIUFx/9oACAEBAAE/EGUjkKYzDt9amN2W2nseE4VRQNVNCbaE/9k='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Technical Debt: we can choose to start paying it off, or incur more interest."
title=""
src="/static/cb7b1025c7567d8ecdb83d4d9573d5d7/64a7b/IMG__0314.jpg"
srcset="/static/cb7b1025c7567d8ecdb83d4d9573d5d7/57443/IMG__0314.jpg 293w,
/static/cb7b1025c7567d8ecdb83d4d9573d5d7/bedd3/IMG__0314.jpg 585w,
/static/cb7b1025c7567d8ecdb83d4d9573d5d7/64a7b/IMG__0314.jpg 1170w,
/static/cb7b1025c7567d8ecdb83d4d9573d5d7/5fd2a/IMG__0314.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Technical Debt: we can choose to start paying it off, or incur more interest.</figcaption>
</figure></p>
<p>Therefore, Mr Fowler plotted the attitudes people would have about such technical debts on a quadrant, segmenting between Prudent or Reckless, then Deliberate or Inadvertent. On the top right quadrant (Prudent and Deliberate), he explained that generally, well-intentioned people would have such an attitude — admitting the fact that they have incurred technical debt, but have to bite the bullet and deliver the product, and mentally aware of the problems and consequences that they need to deal with later.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/1d947cd95a75e00542bf33601ecf4532/5fd2a/IMG__0317.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 44.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAQpOUdKx4v/EABsQAAEEAwAAAAAAAAAAAAAAAAIAAQMUEzEz/9oACAEBAAEFAjcCJ6+K0al5Dtf/xAAWEQEBAQAAAAAAAAAAAAAAAAAAARH/2gAIAQMBAT8Bsa//xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQIBAT8BHJ//xAAcEAABBAMBAAAAAAAAAAAAAAAAAQIREhAhgaH/2gAIAQEABj8CSG1QdEWNL6NOY//EABwQAAICAgMAAAAAAAAAAAAAAAEhABARMUFRsf/aAAgBAQABPyHfaMCGItVKZekLhV9Ff//aAAwDAQACAAMAAAAQF/8A/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8QuP/EABgRAAIDAAAAAAAAAAAAAAAAAAABESFR/9oACAECAQE/ELxD0//EAB4QAAIBAwUAAAAAAAAAAAAAAAERACExYUFRcaHw/9oACAEBAAE/EEfrOEg5uYEt6V22NSIgBQACCzCnbMt+KTbif//Z'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="The Technical Debt Quadrant."
title=""
src="/static/1d947cd95a75e00542bf33601ecf4532/64a7b/IMG__0317.jpg"
srcset="/static/1d947cd95a75e00542bf33601ecf4532/57443/IMG__0317.jpg 293w,
/static/1d947cd95a75e00542bf33601ecf4532/bedd3/IMG__0317.jpg 585w,
/static/1d947cd95a75e00542bf33601ecf4532/64a7b/IMG__0317.jpg 1170w,
/static/1d947cd95a75e00542bf33601ecf4532/5fd2a/IMG__0317.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">The Technical Debt Quadrant.</figcaption>
</figure></p>
<p>There would also be people who are totally unaware of software development best practices and the technical debt they are incurring, hence reckless but inadvertent in nature, as they do not know what they not know. What would be more upsetting, would be people who are aware of their debt, but wilfully choose not to do anything about it, essentially being deliberately reckless about the matter.</p>
<p>One can only get away from the consequences of such wilful stubbornness, when the term of the project is short-lived — short enough before the Design Payoff Line intersects with the project’s velocity curve. This may apply to one-off campaign-related short projects, which one does not need to worry about maintenance.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/fa29949adbe5ffe4b3ec448dd3d8b364/5fd2a/IMG__0316.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 43.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABQBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAUW08RoksH//xAAbEAACAgMBAAAAAAAAAAAAAAAAAQITAwQUMf/aAAgBAQABBQJ1vLN69PTMmLxH/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAh/9oACAEDAQE/AU2f/8QAFhEBAQEAAAAAAAAAAAAAAAAAARAh/9oACAECAQE/ARyf/8QAHBAAAQUAAwAAAAAAAAAAAAAAAAECERIxEFGh/9oACAEBAAY/AshvQtYsb6NF4//EABwQAAICAgMAAAAAAAAAAAAAAAERADEQIUGBof/aAAgBAQABPyEgisiBx0hu4+iXSXjwYf/aAAwDAQACAAMAAAAQBx//xAAWEQEBAQAAAAAAAAAAAAAAAAAAETH/2gAIAQMBAT8Q1K//xAAWEQEBAQAAAAAAAAAAAAAAAAAAITH/2gAIAQIBAT8QxK//xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhMVFxoZH/2gAIAQEAAT8QpVNiLQxrncGXc5EGFtpxGm4ACgPgTzTz5o9z/9k='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="The Design Payoff Line: when cutting corners in design start biting back."
title=""
src="/static/fa29949adbe5ffe4b3ec448dd3d8b364/64a7b/IMG__0316.jpg"
srcset="/static/fa29949adbe5ffe4b3ec448dd3d8b364/57443/IMG__0316.jpg 293w,
/static/fa29949adbe5ffe4b3ec448dd3d8b364/bedd3/IMG__0316.jpg 585w,
/static/fa29949adbe5ffe4b3ec448dd3d8b364/64a7b/IMG__0316.jpg 1170w,
/static/fa29949adbe5ffe4b3ec448dd3d8b364/5fd2a/IMG__0316.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">The Design Payoff Line: when cutting corners in design start biting back.</figcaption>
</figure></p>
<p>Finally, Mr Fowler summarised, when we promise clients to build software with many features but without proper design and architecture, we are ultimately stealing money from our clients: the money — both physical capital and time costs — that the client would need to pay to maintain the software in the future. As software developers, he rallied, we should upkeep our professional ethics to not cause deceit and harm to our clients and users. With that conclusion, he ended the first part of his talk.</p></content:encoded></item><item><title><![CDATA[Revitalizing our engineering - Engineering 2.0]]></title><description><![CDATA[We developed a News Tablet application for the Samsung Knox recently, and we are able to institute the various good engineering practices…]]></description><link>https://sphtech.github.io/revitalizing-our-engineering/</link><guid isPermaLink="false">https://sphtech.github.io/revitalizing-our-engineering/</guid><pubDate>Tue, 12 Mar 2019 14:00:00 GMT</pubDate><content:encoded><p>We developed a News Tablet application for the Samsung Knox recently, and we are able to institute the various good engineering practices into our workflow process. Read on to find out more!</p>
<hr>
<p>For quite some time now, we have visited Govtech who showed us their engineering quarters, and we have attended conferences, and read articles which advocated good engineering practices. While we do agree and recognize its benefits, adopting them require much time, resources and proper planning.</p>
<p>It is with the recent development of the News Tablet application for the Samsung Knox, that we are able to institute the various good engineering practices into our engineering workflow.</p>
<p>As a quick introduction, the News Tablet application is a collaboration between SPH and Samsung, to produce a mobile application exclusively for the Samsung Knox, that allows users to read the various SPH publications’ e-paper on the device.</p>
<h3>Our engineering workflow</h3>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/bfce4012b1552150755589e7f21a1f81/22e61/workflow-disgram.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 33.4375%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABVUlEQVQY05WQO0vDUBzF82H8Bg4uDuqqH0FwqoMgOFgEI7gILmLB+gCFQhUFiy2lg4Ja6qvtUGgr6UNJW9JX+khvwm2b3CQ3N9eUooOD4JkO5/8/w/kxmqbRf8q27bFhGnzFqLT0WsdoyeizYYgAQigriqZAQ2g7udmW9bJo1CWzBVBJxBIk1Or0Bd1UmdJRKDuxlJ9dLy97uMmVD9de4Drgu7p4OjznZ9zF+U1+cbcw5y4ubJVd+/npNWHjTBqAYGqn0Hxh+hle9Nx0/fcgkhBYXzf0yuW4VCYtpN47p7dN350SjtdPwtJltBdONI4j8kMaYZTkg9Uex6iGCQa6SinMluJTqyCaRZQ0a3VnEhgiqGNM6ePbsyhLyLTBUEcW+RnPVAXBMjCxKap1+W3/oFB1jpqqjsBYZMzGe+DNcdw4od+0RmVCyN9UMcYsy8ZiMcf/ev4CLdNvhl8DHTwAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="We have a executable build for every commit in main branches."
title=""
src="/static/bfce4012b1552150755589e7f21a1f81/1c968/workflow-disgram.png"
srcset="/static/bfce4012b1552150755589e7f21a1f81/eb41c/workflow-disgram.png 293w,
/static/bfce4012b1552150755589e7f21a1f81/7110a/workflow-disgram.png 585w,
/static/bfce4012b1552150755589e7f21a1f81/1c968/workflow-disgram.png 1170w,
/static/bfce4012b1552150755589e7f21a1f81/22e61/workflow-disgram.png 1600w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">We have a executable build for every commit in main branches.</figcaption>
</figure></p>
<p>We employ Bitrise (many thanks to our QA engineers who did various product studies and recommended this tool) as the central orchestrator to perform Code Linting, calling up Codecov to execute Unit Tests, perform a test to build the app, initiate a Code Review session on Github, and after completing the review, generate a release build for the app store. It essentially serves as the main conveyor belt that carries our code from our hands into the end product.</p>
<p>Quite a number of processes isn’t it? Let us observe some of the salient processes in detail.</p>
<h4>1. Code Linting</h4>
<p>Back in our school days when we needed to do projects, each team member would work on different parts of it — some would compile and analyse the statistics, some would write the introduction, the findings and conclusion — and on nearing the submission date, discovered that there were various spelling or grammatical errors (that MS Word could not detect, or the team member blatantly ignore), and some parts of the facts or data were wrong. There would be the team leader, who would do the final rounds of checking and correcting of these errors. It was a very manual process.</p>
<p>In our context, to make sure our codes do not have “grammar” errors, we employ code linters to do the checks. Whenever one of us puts in new codes or changed existing ones, the linters would run and analyse these codes. Should there ever be any “grammar” errors, it would alert the team member, and stop carrying the code into the next step. As such, each team member is responsible for writing “grammatically correct” codes right at the beginning of the workflow, instead of deferring it until the later QC/QA stage.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/6f84dc758527d0f30ad1b38298c35857/787b5/pass-linting.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 727px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 17.881705639614857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsSAAALEgHS3X78AAAAmElEQVQY04XOQQ7CIBAFUA5krQVt4sLGUilh00SBtsREE7X3P8IXlBpl4+JlJn8IM4S1FpvWoZTO1xFl7NdiwIqbHwW3L2n+URuQSjvw4wPCTqhPNzTmDtFP2HUXsEMP+oVFNM398nBAmJHc/8zkgEwZLKR+UxqZ0Mj3/y29cPVWnVF1V5BwatgQNhWNjWLPE/M8yal/P3sCLLaP/N6aQXwAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="An example of new codes that have passed linting."
title=""
src="/static/6f84dc758527d0f30ad1b38298c35857/787b5/pass-linting.png"
srcset="/static/6f84dc758527d0f30ad1b38298c35857/eb41c/pass-linting.png 293w,
/static/6f84dc758527d0f30ad1b38298c35857/7110a/pass-linting.png 585w,
/static/6f84dc758527d0f30ad1b38298c35857/787b5/pass-linting.png 727w"
sizes="(max-width: 727px) 100vw, 727px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">An example of new codes that have passed linting.</figcaption>
</figure></p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/65c8bb35e0a7f2e5c0c3c5900e6fad55/02bf6/fail-linting.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 22.033898305084744%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsSAAALEgHS3X78AAAA60lEQVQY01WQK08FMRCF9zehUfwNEhy5BoG4ILAIcCgsDgJBEAyCxxUYbjBIEiAhARSE181uuzvt9GOmjqYnp9OZOWfaJi9uontnKKCzllIgfr4QnyaUtyn6evMP+fmacHdLeHwgqTIMA33fV6SUaMrcMmztwyAm8I6vn/tzvo+WYLJOuRyDoVyswdUYPR3xsb3B1+EBYrWh62jbtsJFm7KwStk9IVnSHSSJcTZ3yDZtyooY1M7Oydh2hdqEIlL7HB43Or9C2TkmWkH3OyOGSAihumUTcI4x2leU+jyRocp5nHOmswk97z1+9wfSLigUMguUIQAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="An example of new codes that have failed linting from the Bitrise dashboard."
title=""
src="/static/65c8bb35e0a7f2e5c0c3c5900e6fad55/02bf6/fail-linting.png"
srcset="/static/65c8bb35e0a7f2e5c0c3c5900e6fad55/eb41c/fail-linting.png 293w,
/static/65c8bb35e0a7f2e5c0c3c5900e6fad55/7110a/fail-linting.png 585w,
/static/65c8bb35e0a7f2e5c0c3c5900e6fad55/02bf6/fail-linting.png 590w"
sizes="(max-width: 590px) 100vw, 590px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">An example of new codes that have failed linting from the Bitrise dashboard.</figcaption>
</figure></p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/fcd8e07e79b91b88ac2b6134eb21083c/22e61/fail-linting-details.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 50.6875%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABKklEQVQoz32R63KCMBCF8z4VreAFTJBLREQqhMpFiC31/V+iJ1FHfzDMfJPZTWZ3z9kQe9/SRDpxh2DJ66krJpvMoJnB8ikT4xCaXn1xc79+7aRDzNIf+9Ato+bTP0+YMNDLVecgxOK1k8hVdFnvL/ps13GLeMEri1dmWC54DebBeaB4HpQY6xykGVbTrdAUYKZRgfetb4Ym3wuUPCY+aKbJNdmE5SOoYtjbnvpNIu24tQ+gWyn9DVI8LXa1gtdWWJlBCXVI4RSO5v4Znit6vHqn3s//NDfU4xLFcATNmuLuRbsoHqlbEGzV0BrUqRDG08jDufeqmW1fXQDBBGx7c5QYiN9CjLFWCIUlNqxFIlWbvwe4t54fQVjcRWnvxZLuGsob9B7f0/vC/gH9OZpsQ0W3IwAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="The linter would point out which parts of the codes failed, along with suggestions on how to correct it."
title=""
src="/static/fcd8e07e79b91b88ac2b6134eb21083c/1c968/fail-linting-details.png"
srcset="/static/fcd8e07e79b91b88ac2b6134eb21083c/eb41c/fail-linting-details.png 293w,
/static/fcd8e07e79b91b88ac2b6134eb21083c/7110a/fail-linting-details.png 585w,
/static/fcd8e07e79b91b88ac2b6134eb21083c/1c968/fail-linting-details.png 1170w,
/static/fcd8e07e79b91b88ac2b6134eb21083c/22e61/fail-linting-details.png 1600w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">The linter would point out which parts of the codes failed, along with suggestions on how to correct it.</figcaption>
</figure></p>
<h4>Unit Testing</h4>
<p>We remember when we were tackling long story problem sums, where each statement would entail us drawing some model diagram and doing some math calculation: while we have generally used the correct steps in solving the problem, we were waylaid by a careless mistake in our calculation in one of the steps (e.g. forgot to include the remainder of a long division), that led to an incorrect final answer. Our teachers would always sigh and remind us to always “check our answers again”.</p>
<p>Unit tests, in this sense, help us to be sure that our individual problem solving steps always give us the correct and expected answers. We would also wish to highlight that unit tests zooms in on the smaller (or even smallest) units of our code base, essentially testing the individual functions by itself. Hence, when one of us writes new codes, the new codes should have accompanying unit tests to prove its integrity.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/2c54f5b0dc2a426b01e7e5e986b92258/f4e82/happy-path.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 948px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 20.675105485232066%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsSAAALEgHS3X78AAAAvklEQVQY023OSXKDMBCFYY5jJBCYSbQGJLApV+5/oD8K9iKVyuKr7rfooZp7zeEXoqzkwtvSu5nNW6KfcetAjjNBRpwdCOuInTpkfltG89HRGkOllCL5zLE9eKaTIz1JKZP3AxFhmoZSLbYcskthLX3f0ZnmYlr90aC1fi8U8eWbjJfIujokRFzYEB+4j3eM6VBKc7vdipq6Vlf+mf2rapoG5yNn/uKML2JIhLST9vLp/mCyE23bXtf/VZb8zt/g84BSB1ZKtQAAAABJRU5ErkJggg=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="An example of “happy path” (i.e. default scenario with no exception or error conditions) test case, which verifies that the intended functionality works."
title=""
src="/static/2c54f5b0dc2a426b01e7e5e986b92258/f4e82/happy-path.png"
srcset="/static/2c54f5b0dc2a426b01e7e5e986b92258/eb41c/happy-path.png 293w,
/static/2c54f5b0dc2a426b01e7e5e986b92258/7110a/happy-path.png 585w,
/static/2c54f5b0dc2a426b01e7e5e986b92258/f4e82/happy-path.png 948w"
sizes="(max-width: 948px) 100vw, 948px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">An example of “happy path” (i.e. default scenario with no exception or error conditions) test case, which verifies that the intended functionality works.</figcaption>
</figure></p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/179b2bd29e4a7e35a4856a91e5f443cb/f4e82/unhappy-path.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 948px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 18.354430379746837%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsSAAALEgHS3X78AAAAqklEQVQY022N/Q6CIBTFfR0QEFHELBURs7be/3VOV7Taqj9+Ox/3bDcbmgKtLdFZjd6ZpF2z42q61QoXR9kWOBEd7V2lkt/6ljbNkU2pkY3tiNhHBD8jTgv84DGMI+blCmMMpODQSqDYkLuqQ3Uhk0+ZkEIg8+eINTwQrndMcUWYbwk/rxBSgTEGxvkPPJG//UszXZaoagtT1agIaxrC0TeZBnme/+HTf2+eQdd+6id2N1sAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="An example of “unhappy path” test case, very valuable, but sadly quite often not taken into consideration by developers during crunch time."
title=""
src="/static/179b2bd29e4a7e35a4856a91e5f443cb/f4e82/unhappy-path.png"
srcset="/static/179b2bd29e4a7e35a4856a91e5f443cb/eb41c/unhappy-path.png 293w,
/static/179b2bd29e4a7e35a4856a91e5f443cb/7110a/unhappy-path.png 585w,
/static/179b2bd29e4a7e35a4856a91e5f443cb/f4e82/unhappy-path.png 948w"
sizes="(max-width: 948px) 100vw, 948px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">An example of “unhappy path” test case, very valuable, but sadly quite often not taken into consideration by developers during crunch time.</figcaption>
</figure></p>
<p>Codecov — the tool we use to execute unit tests — is also helpful to indicate areas of our codes where we need to write tests. The coverage report visibly shows code changes covered by unit tests. Hits and misses are shown on the left-most side, in darker green and red colors respectively.</p>
<p><a
class="gatsby-resp-image-link"
href="/static/2db65ff6ddf94bd75a9f1c806882bdce/b38ff/codecov-details.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1130px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 58.23008849557522%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsSAAALEgHS3X78AAABgElEQVQoz42Ti26rMAyGef8H3Fb19LJCuOYCBQoBkn+OKdUqVetR9Ml2fFXA0bXtYe2CeQHs5DARzgOecBsOWP6DefGI2l5hnK5oOwljctRNgeZa4tpKdL1GdzPoB8Mx72kRZX0CPVdI2xiHfIdTtcdZHpDU34xoLsg7wTF6kUS16i+oZ4XIaw2YGjQavFKwQmCIE7iqonsDLyVcSXqIY8wa/wLfNIgWJeG1euDInosc3fmE9nRE/31mhiTGIKiRDI30U84jtzaI3L3gFhQkyGFTgVt8gc1STHmGuSy42MYj5w62gjyhUXc0TxiYaMpBxBjThBBccCpzJug8lXmGC0JSdXV/G5KuKAGpVvsv1Br/BL1jVLQXqCGFHjOU9KWFPiBrzpA3wfebDP53mLFE9Dl+4eCPOOLEMthfdofdvMfH8Mn2fvnH/ndcfIwoDd2dgqajvEI2ZBC9YJm0CUsZ/r9w/N/Ujt7Qjhae9svT2njeM6w27x8e9u/7VwS/o/37AYojl92HcWLvAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="codecov details"
title=""
src="/static/2db65ff6ddf94bd75a9f1c806882bdce/b38ff/codecov-details.png"
srcset="/static/2db65ff6ddf94bd75a9f1c806882bdce/eb41c/codecov-details.png 293w,
/static/2db65ff6ddf94bd75a9f1c806882bdce/7110a/codecov-details.png 585w,
/static/2db65ff6ddf94bd75a9f1c806882bdce/b38ff/codecov-details.png 1130w"
sizes="(max-width: 1130px) 100vw, 1130px"
/>
</span>
</a></p>
<p>During the initial phase when we were starting out with the project, we only covered 19% of our codes with unit tests. With our workflow in place, our tests coverage grew steadily to 41% after 3 months! Seeing the chart line ascending is very encouraging.</p>
<p><a
class="gatsby-resp-image-link"
href="/static/2e587890d44d1ec5e78b91c3b8758831/91892/codecov-progress-chart.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 915px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 64.15300546448087%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsSAAALEgHS3X78AAACCUlEQVQ4y5WTyW7bMBCG9cQFWt8KFDkUueR1WqCHFjAc9NYladIEtRvHkiVbtmVtlLho/TskbTXxqaXwYShyZkjM/HS44MiyDGVZQggxwDWcQxB2TuvyuMcNz/3tmqMqBakkmrYZaLsWHXrUCYPKCzAhkTABSQm1f9d3xucUHTskfDp0gKHTHAP+JjF7dKDGfoc4mpmEBS+RshThNkScxvi3QWkoOVq6WVWjkhWauobT0mm+u8T567cYvXiFi/MLVFxBxAw8ysHWCaLHLRIvQubtEP1eYztdYXXnY30fGBv89OB+nSNZxXCarkEa7rH4MoV79UDMsb5bIrj3sSSCXwHC2cqwMZb+pwQlDueW3WKDDdmCau4UZYEsz8F1B6mWupMldaukMnAlzLrpqLRdPO49gxRSHHC0JFjBTOullBZKrGsrUjooZ2DkmOY2WEtJKdtIpazvAMU6VV1BY8v89KN6lyQnkoykYpeiQt1Q4ZvG7veWQREHBtlop9PN3vbyv4ajkwmq1fGGJ8JAr/UnSMx0y1ZVkDnVMyvBSAEVU/j07iPO3pxh9HKEyXgCp25rFHGO/Yy6NdWSIEvzkOSwul3C/+HBI/wbD+tbD8GNC/96AffbHMurR1yPv+Pzh0tM3o/hzVx7wyxOEW/2SLaxIT7YgShBsrOk+9SgY7QtsgKSnqWgm0t6738AjlbgG9xyYy0AAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="codecov progress chart"
title=""
src="/static/2e587890d44d1ec5e78b91c3b8758831/91892/codecov-progress-chart.png"
srcset="/static/2e587890d44d1ec5e78b91c3b8758831/eb41c/codecov-progress-chart.png 293w,
/static/2e587890d44d1ec5e78b91c3b8758831/7110a/codecov-progress-chart.png 585w,
/static/2e587890d44d1ec5e78b91c3b8758831/91892/codecov-progress-chart.png 915w"
sizes="(max-width: 915px) 100vw, 915px"
/>
</span>
</a></p>
<h4>Code Reviewing</h4>
<p>It is only after the new codes have gone through the automated steps of the workflow successfully, do we step in to conduct the manual activity of Code Review. It is also in this step where another team member is roped in to look at the new codes that the original author has written.</p>
<p>We take code reviews as opportunities for each of us to show our team members how we solved a particular problem or implemented a function. From this sharing, we invite constructive feedback, either to affirm our solution methodology, or to point out areas that need improvement. Hence, good coding practices are propagated to the whole team, and the whole team benefits!</p>
<p>At the end, after the other team member has gone through the code review and gives his/her “LGTM”, the Pull Request (PR) is approved and merged back to the dev or master branch for the application build to be generated.</p>
<h3>Next steps</h3>
<p>As mentioned earlier, it took a considerable amount of effort and time to get to where we are currently. With the workflow process in place, we would need to work on refining the individual steps in it: we plan to do more refactoring of our codes, so as to make them more maintainable and more unit testable. We are also in the process of putting in a code analysis tool to pick out code smells — potential bad coding structure or habits — so as to provide feedback to ourselves as to where we can write better, industry-accredited code.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/fdc92ab97a62f8b38692afac093c2246/b8efc/sonarcloud-details.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 897px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 40.245261984392414%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsSAAALEgHS3X78AAABTElEQVQoz5WSW3OCMBCF+f9/q6PWKlalWscLiKABTCSEuy+nB9oHZzpOpw/f7CWTzdndWDI6IBEbXM8ryNMcKnRQyC3KW8cOtT6gzdyehv4jVbpHnR7IHm2+xb30YUWuB0Hi3R7Rwe39jrCLXRfqFEBfBPIoRisVGil7WmKYE8cQie+jzliwOsIqsgBpskZ2/USZUhGVVN3rPVRAOr9T2nQqH2iNx7zbn90LUp1gZec3iMsI/vEFIhgiDgZQYogsGeMWj1HcbF58Z4Hf1I++nqExa1jmYqPUc6q0kUsb5mqjULOeTC2oesELRP/YZ7BGYzawmvqCOFpCyTUKzZbNHnm67Sn1FhXje+Vx4N63fUbp0gawtD+AUVMk5yGkGCEJGSevqG4TbnlCO+UW/8ImE47gA1aeOFTiQCsHOVs0cs42l/wGK7bwHxw0+Q5fumlSwLPKBGAAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="We are using Sonarcloud to pick out code smells."
title=""
src="/static/fdc92ab97a62f8b38692afac093c2246/b8efc/sonarcloud-details.png"
srcset="/static/fdc92ab97a62f8b38692afac093c2246/eb41c/sonarcloud-details.png 293w,
/static/fdc92ab97a62f8b38692afac093c2246/7110a/sonarcloud-details.png 585w,
/static/fdc92ab97a62f8b38692afac093c2246/b8efc/sonarcloud-details.png 897w"
sizes="(max-width: 897px) 100vw, 897px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">We are using Sonarcloud to pick out code smells.</figcaption>
</figure></p>
<p>We would also need to reach out, assist and build confidence in other teams in our department to adopt this workflow process, by setting up the linting and application building steps. Getting these easier to achieve workflow process steps in place will help our colleagues to be more familiar with this new workflow. With the new found confidence then can more of us get started with writing unit tests, which is the trickier part.</p>
<p>There is definitely more to share, and also more to be done in our efforts to revitalize our engineering. We will cover more details in our future posts, so do watch this space!</p></content:encoded></item><item><title><![CDATA[About Us]]></title><description><![CDATA[This is the beginning of an exciting era for our SPHTech team!Over here, you will get to read about our learnings and reviews of the new…]]></description><link>https://sphtech.github.io/about-us/</link><guid isPermaLink="false">https://sphtech.github.io/about-us/</guid><pubDate>Tue, 01 Jan 2019 23:46:37 GMT</pubDate><content:encoded><p>This is the beginning of an exciting era for our SPHTech team!</p>
<p>Over here, you will get to read about our learnings and reviews of the new technologies we are playing with, the meet-ups and conferences we attend, some of the presentations that we do on a regular basis and many other great content!</p>
<p>We believe that the information and knowledge learned by the individual can be put to great use when they are being shared with the rest of the team. This platform serves to connect any of us who share the same interest in certain topics, to propagate and champion for the usage of new technologies, as well as to promote the adoption of technology, security and programming best practices!</p>
<p>May you have an enjoyable time here reading the blog articles!</p></content:encoded></item><item><title><![CDATA[The developer's role in a successful implementation of the test automation]]></title><description><![CDATA[Rod Michael, director of global market access strategy and channels at Rockwell Automation, famously said, “If you automate a mess, you get…]]></description><link>https://sphtech.github.io/developers-role-in-test-automation/</link><guid isPermaLink="false">https://sphtech.github.io/developers-role-in-test-automation/</guid><pubDate>Thu, 06 Dec 2018 12:54:00 GMT</pubDate><content:encoded><p>Rod Michael, director of global market access strategy and channels at Rockwell Automation, famously said, </p>
<blockquote>
<p>“If you automate a mess, you get an automated mess.”</p>
</blockquote>
<p>A meaningful and therefore effective automated test system is created with intelligent design. It is not something that just happens.</p>
<p>As we all know, testing processes are important in order to deliver a bug-free application. Amplifying its impact, automated testing is beneficial in saving build validation time and effort, as well as helpful in demystifying crucial performance hurdles in the application. Eventually, this paves the way to deliver outstanding softwares.</p>
<p>Hence, it is crucial for a test engineer to create an effective road map and choose the right tools to build a robust test automation framework. Besides the test engineers, developers also play a vital role in the successful implementation of the automation framework.</p>
<p>There are several guidelines/principles available online to guide a test engineer to effectively strategise and implement the test automation framework. However, in this post, we would like to shed some light on the contribution that is required from fellow developers.</p>
<p>Some development practices that set the foundation to naturally drive the automation are:</p>
<ol>
<li>Make sure the app is CI/CD ready (can be deployed anywhere).</li>
<li>Use APIs to help automation with quick setup/cleanup.</li>
<li>Follow naming conventions/standards to intuitively allow automation engineers/tools to determine the element ID.</li>
<li>Write design documents for new features, involve at least 1 automation engineer to review the same and actively involve in test case review processes.</li>
<li>Have a centralise repository (that is easily accessible) to check-in/check-out development source codes.</li>
<li>Move towards Behavioural Driven Development (BDD) - product owner/developers to come up with the set of user stories for the feature to be developed. That would help test engineers to map it to their Cucumber feature file.</li>
<li>Write unit test cases to ensure the first hand validation of the feature and provide a baseline for automation engineers to improvise further.</li>
</ol>
<p>Some of the immediate action items for developers to contribute towards effective software automation are:</p>
<h3>1. Always assign unique locators for page elements in a single view</h3>
<p><strong>How:</strong></p>
<p><a
class="gatsby-resp-image-link"
href="/static/6edd066752220a341bee836a46884a85/51a3e/blog-test-automation2.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 58.785714285714285%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACGUlEQVQoz4WTy2sTURjF70oX2qaG6qI+liqljTEutCtrUBRFFIrmUa2bQjcuxVgQCq2CCwUf+AfYhYKCOxFcuHPnv1GS5jFJ5nFnJjOT4z03nUmTjR8c5s6de3+c7zHiarmCfHEdl+9XsFh4hoW7T7D97Rd6nkSj2YR0XUgp4TjOiCzLgmEYqNfr6Ha7sG1b74tUpoBDswUcni2Ca3HiBl59/ApG12hDqsO+6yEIQvT7fcQRRZGW7/vwPA9hGOp98fz1J0xlyjhzZQ2nLlUgTq5h891n/dFst9Ha2YFjmogUjABCKQLonKI7vlNiIX8b6dwjBS3iyLlViJkCXrz/ooHSkTCqVVitFsaDl3u9nhZTpUuuxdncogam5kuYPl+GOH49AfKAqWCmqqXruElqdBoEgX5nynTJs3Qurt25h6nsinJYUg4VcObmCLCjit6pVeHZEp5qEAGxMwIJdtV+XFORyZeQzj7A5FwRxy6sKIdDYKAOGG0Lu7stNAwbTVMmjeHl2CHhDO3w4tK6dpZSjZnOPRwB2qaFTqOpn2HU143Z3xSC6JBKgKuVD5iYY0OWcXQM6Kqm+ExnbyTUlcQhgXEj9oPF46cvMTFf1imns8u6hlt7QLqKY+AMCZAQDvS4xI/ff3Hg9JKGDWp4C5tvB3MYhGGS4hA6BPJPGZf4/vMPDirgpE67rIEbb7b/C+SoVNWM1mo1/Yz1DyELGLxLgt9oAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="blog test automation2"
title=""
src="/static/6edd066752220a341bee836a46884a85/1c968/blog-test-automation2.png"
srcset="/static/6edd066752220a341bee836a46884a85/eb41c/blog-test-automation2.png 293w,
/static/6edd066752220a341bee836a46884a85/7110a/blog-test-automation2.png 585w,
/static/6edd066752220a341bee836a46884a85/1c968/blog-test-automation2.png 1170w,
/static/6edd066752220a341bee836a46884a85/51a3e/blog-test-automation2.png 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a></p>
<p>i. As shown above, every element in application view have identifiers to locate from backend.</p>
<ol>
<li>For iOS apps, it is the Accessibility ID</li>
<li>For Android apps, it is the ID</li>
</ol>
<p>ii. It is always a best practice to have a unique value to such identifiers (or at least try to have it unique across the same page/view).</p>
<p><strong>Why:</strong> Unique locators allow better navigation.</p>
<h3>2. Avoid creating dynamic IDs for page elements, which keeps changing with view/session reload</h3>
<p><strong>How:</strong> Dynamic IDs are worse than non-unique locators, as that serves as anti-automation practice. Use static locators (such as IDs instead of Xpath).</p>
<p><strong>Why:</strong> Static locators contributes to robust automation framework.</p>
<h3>3. Always provide a proper title to window or screen</h3>
<p><strong>How:</strong> Follow standard naming conventions for element IDs and locators.</p>
<p><strong>Why:</strong> For automation engineers to be able to intuitively determine the element locators in future, allowing:<br>
i. feature development and automation to work in parallel,<br>
ii. minimal dependency and work efficiently in agile.</p>
<h3>Conclusion</h3>
<p>Hence, the developers has the important responsibility to make object identifications easy and make application behaviour predictable. These pointers, if taken care of, will help test automation to be more efficient, and the test automation framework to be more robust. Otherwise no matter how good the test automation architecture or tool is, it can not help much and will not be effective.</p></content:encoded></item><item><title><![CDATA[Towards a Privacy Aware Smart Nation]]></title><description><![CDATA[On the 21st of November, SPH staff gathered for a cozy SPHTech Meetup session at The Inspiration Hub to learn about Data Privacy efforts in…]]></description><link>https://sphtech.github.io/towards-a-privacy-aware-smart-nation/</link><guid isPermaLink="false">https://sphtech.github.io/towards-a-privacy-aware-smart-nation/</guid><pubDate>Thu, 29 Nov 2018 12:43:00 GMT</pubDate><content:encoded><p>On the 21st of November, SPH staff gathered for a cozy SPHTech Meetup session at The Inspiration Hub to learn about Data Privacy efforts in Singapore.</p>
<p>Professor Anthony Tung, Associate Professor at National University of Singapore (School of Computing), shared insights on moving “towards a Privacy-Aware Smart Nation”. During the session, Prof Tung introduced the newly developed N-CRiPT (NUS Centre for Research in Privacy Technologies) which aims to develop privacy-preserving technologies to protect identities and sensitive data with greater focus on unstructured data throughout the data life cycle.</p>
<p>N-CRiPT is a crucial step for Singapore as R&#x26;D in developing privacy-protecting systems in tandem with technology advancement will help to ensure sustained growth in moving forward as a Smart Nation. Prof Tung also shared about the prevalence of the risks involved in the world of big data we live in today, leaving a lasting impression on those present at the meetup.</p>
<p>This meetup session is just one of many planned by SPHTech to promote a spirit of innovation and digitisation to improve our efficiency and effectiveness in the way we work. Do look out more more sessions to come!</p></content:encoded></item><item><title><![CDATA[Docker workshop in SPH]]></title><description><![CDATA[Containerization has been one of the most talk about topics in the digital world. It enabled a whole new paradigm shift in getting software…]]></description><link>https://sphtech.github.io/docker-workshop-in-sph/</link><guid isPermaLink="false">https://sphtech.github.io/docker-workshop-in-sph/</guid><pubDate>Tue, 27 Nov 2018 11:06:00 GMT</pubDate><content:encoded><p>Containerization has been one of the most talk about topics in the digital world. It enabled a whole new paradigm shift in getting software to run reliability when moving from one environment to another.</p>
<p>I have the privilege to run 2 docker workshops in SPH as I managed to set up docker running zaobao.com locally on my PC. It has been an amazing experience.</p>
<p>The first docker workshop was held on 19th October 2018, with 23 people attending. We went through what is containerization and its difference with virtual machines. Then we have exercises on docker command, docker-compose.yml and set up a completely new drupal website from scratch. And we have great food at the end!</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/76090f8ae29d30e67b6d37e3cb3c29c2/5fd2a/IMG_9182.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 133.35714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAADAf/EABcBAQEBAQAAAAAAAAAAAAAAAAIAAQP/2gAMAwEAAhADEAAAAcmHz5pEeRPn3kk1Zf/EABwQAQACAgMBAAAAAAAAAAAAAAIAAQMSERMjMf/aAAgBAQABBQLBroOCnWNpWSdzPOpmuD7pvOxVOxSmuP/EABcRAAMBAAAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8BRl//xAAWEQADAAAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8BNqK//8QAHxAAAQMDBQAAAAAAAAAAAAAAAAECEQMQMRIhMmHh/9oACAEBAAY/Aocm5LVNSVYkSTlZqdWkyeGT/8QAHBABAAMBAAMBAAAAAAAAAAAAAQARMSFBYZFx/9oACAEBAAE/IdSV251qepQkdDDyxeUQRo+hjejxlrRkLFXGgB4phwGMsGXvU+JmU/J//9oADAMBAAIAAwAAABAA5TL/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAEDAQE/EEUhAqnTT//EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8QhSIhKoYj/8QAHhABAAMAAgIDAAAAAAAAAAAAAQARITGBQVGh4fD/2gAIAQEAAT8QLKS29OAMA0xynElU+CJ58xFXzuoMlVOaBu42sJ0j4ipFMVt57j1oVYeu5telir0+qhVBMYQ7JX2/x6gQKDgAHxP/2Q=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Food! Glorious Food!"
title=""
src="/static/76090f8ae29d30e67b6d37e3cb3c29c2/64a7b/IMG_9182.jpg"
srcset="/static/76090f8ae29d30e67b6d37e3cb3c29c2/57443/IMG_9182.jpg 293w,
/static/76090f8ae29d30e67b6d37e3cb3c29c2/bedd3/IMG_9182.jpg 585w,
/static/76090f8ae29d30e67b6d37e3cb3c29c2/64a7b/IMG_9182.jpg 1170w,
/static/76090f8ae29d30e67b6d37e3cb3c29c2/5fd2a/IMG_9182.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Food! Glorious Food!</figcaption>
</figure></p>
<p>I also demonstrated that it was possible to run zaobao and zaobao.sg on the same container with the help of <a href="https://traefik.io/">traefik</a> on localhost.</p>
<p>The objective of the part 1 familiarisation was to prepare for the docker workshop 2. In that workshop, we got the participants to run their actual drupal sites. Yes, actual BT, ST, BH sites on our laptops and the same docker images can be deployed to Staging and Production servers on Amazon Web Services with the help of <a href="https://aws.amazon.com/ecs/">ECS</a>.</p>
<p>On 2nd November 2018, I ran the second docker workshop. Before that day, I did nag the developers to get their production code and database of their drupal sites so that they can get the best value out of their workshop. On that day, they did bring their stuff, except we have an unexpected guest:</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/08d6f88e3486ef1d69d5f4439cd0bcb2/5fd2a/blackout.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 56.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGQwnsaYiRX/8QAGRAAAgMBAAAAAAAAAAAAAAAAAQIAAzES/9oACAEBAAEFAhps6IsRgYmT/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BiP/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/AWf/xAAYEAADAQEAAAAAAAAAAAAAAAAAATERIP/aAAgBAQAGPwLSIi4//8QAGRABAAMBAQAAAAAAAAAAAAAAAQAQEUGB/9oACAEBAAE/IVh5GO0NgB5RU//aAAwDAQACAAMAAAAQky//xAAWEQEBAQAAAAAAAAAAAAAAAAABEDH/2gAIAQMBAT8QBk//xAAXEQEAAwAAAAAAAAAAAAAAAAAAASFR/9oACAECAQE/EJap/8QAGhABAQEBAQEBAAAAAAAAAAAAAREAITFRcf/aAAgBAQABPxC5IBYdeaaAKVnTn3GZbnks3l/TICM0Dw3/2Q=='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Blackout!"
title=""
src="/static/08d6f88e3486ef1d69d5f4439cd0bcb2/64a7b/blackout.jpg"
srcset="/static/08d6f88e3486ef1d69d5f4439cd0bcb2/57443/blackout.jpg 293w,
/static/08d6f88e3486ef1d69d5f4439cd0bcb2/bedd3/blackout.jpg 585w,
/static/08d6f88e3486ef1d69d5f4439cd0bcb2/64a7b/blackout.jpg 1170w,
/static/08d6f88e3486ef1d69d5f4439cd0bcb2/5fd2a/blackout.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Blackout!</figcaption>
</figure></p>
<p>Despite the set back, we were still able to achieve our objectives. Swee Hock was able to get ST dockerized locally and present to us all. BT and TNP were able to be dockerized as well during the workshop.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/998e77f1437fd97ec8d15510d0620527/5fd2a/IMG_0075.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABw+jxGuRyD//EABoQAAIDAQEAAAAAAAAAAAAAAAECAAMSERP/2gAIAQEAAQUCZ74jZT1Bl1+VFhcdE//EABURAQEAAAAAAAAAAAAAAAAAAAEA/9oACAEDAQE/AQlv/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/AbWv/8QAHBAAAgICAwAAAAAAAAAAAAAAAAECESEiMVFS/9oACAEBAAY/AtXIuXNZLQ/QpSnSa6NJOj//xAAaEAEAAgMBAAAAAAAAAAAAAAABABEhMUFR/9oACAEBAAE/IcqNg0bQ10ESYAHjs2G8DqUxv//aAAwDAQACAAMAAAAQcw//xAAWEQEBAQAAAAAAAAAAAAAAAAARAAH/2gAIAQMBAT8Q0SDS/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPxDJUg//xAAaEAEBAQEAAwAAAAAAAAAAAAABESEAMVGx/9oACAEBAAE/ELcCwEv3qjss4vHFMRHlcUQCMb5vYEk2kKenOnAE0yd//9k='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Achievement unlocked"
title=""
src="/static/998e77f1437fd97ec8d15510d0620527/64a7b/IMG_0075.jpg"
srcset="/static/998e77f1437fd97ec8d15510d0620527/57443/IMG_0075.jpg 293w,
/static/998e77f1437fd97ec8d15510d0620527/bedd3/IMG_0075.jpg 585w,
/static/998e77f1437fd97ec8d15510d0620527/64a7b/IMG_0075.jpg 1170w,
/static/998e77f1437fd97ec8d15510d0620527/5fd2a/IMG_0075.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Achievement unlocked</figcaption>
</figure></p>
<h3>What’s next?</h3>
<p>We will be looking into using docker for running scripts. And we will also be looking to creating our very own Dockerfile for our various tech uses and upload the images to ECR.</p>
<p>Also we will be looking at how to use containerization to push the latest production database to developers without compromising security so as they have the latest data to work on.</p>
<p>Special thanks to Ken for coaching me on events planning, Niro and Hector for being Technical Assistants for my workshops!</p></content:encoded></item><item><title><![CDATA[Using Mockito's ArgumentCaptor on SingleLiveEvent]]></title><description><![CDATA[A class is an observable live data that can be used to send events from ViewModel to View in Android MVVM-styled designs.This link…]]></description><link>https://sphtech.github.io/using-mockitos-argumentcaptor/</link><guid isPermaLink="false">https://sphtech.github.io/using-mockitos-argumentcaptor/</guid><pubDate>Wed, 10 Oct 2018 17:21:00 GMT</pubDate><content:encoded><p>A <code class="language-text">SingleLiveEvent</code> class is an observable live data that can be used to send events from ViewModel to View in Android MVVM-styled designs.</p>
<p><a href="https://medium.com/@trionkidnapper/issuing-commands-from-a-viewmodel-using-kotlin-sealed-classes-f1bbab7d4979">This link</a> describes using Kotlin’s sealed data class with a <code class="language-text">SingleLiveEvent</code> to create such a mechanism.</p>
<p>I used that in one of my projects and ended up with a ViewModel that looks like this:</p>
<h4>ViewModel Class</h4>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token keyword">val</span> command<span class="token operator">:</span> SingleLiveEvent<span class="token operator">&lt;</span>Command<span class="token operator">></span> <span class="token operator">=</span> dependencyProvider<span class="token punctuation">.</span><span class="token function">getCommmand</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// This value will be set by other business logic</span>
<span class="token keyword">var</span> todayDate <span class="token operator">:</span> String<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span>
<span class="token keyword">fun</span> <span class="token function">handleButtonClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
todayDate<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">{</span>
command<span class="token punctuation">.</span>value <span class="token operator">=</span> Command<span class="token punctuation">.</span><span class="token function">LaunchNextActivity</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<h4>Command Sealed Class</h4>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token keyword">sealed</span> <span class="token keyword">class</span> Command <span class="token punctuation">{</span>
<span class="token keyword">class</span> <span class="token function">LaunchNextActivity</span><span class="token punctuation">(</span><span class="token keyword">val</span> dateString <span class="token operator">:</span> String<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">Command</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre></div>
<h3>How does it work?</h3>
<p>A user event will trigger this <code class="language-text">handleButtonClick()</code> in my ViewModel class.</p>
<p>It must then instruct a View to launch NextActivity using a variable <code class="language-text">todayDate</code>.</p>
<p>The <code class="language-text">todayDate</code> value is set by some other UseCase class.</p>
<p>I am using a simple Kotlin object class to provide dependencies, <code class="language-text">SingleLiveEvent&lt;Command&gt;</code> type class being one of them.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token keyword">interface</span> DependencyProvider <span class="token punctuation">{</span>
<span class="token keyword">fun</span> <span class="token function">getCommmand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> SingleLiveEvent<span class="token operator">&lt;</span>Command<span class="token operator">></span>
<span class="token punctuation">}</span>
<span class="token keyword">object</span> Injector <span class="token operator">:</span> DependencyProvider <span class="token punctuation">{</span>
<span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getCommmand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> SingleLiveEvent<span class="token operator">&lt;</span>Command<span class="token operator">></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">SingleLiveEvent</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre></div>
<h3>Writing a Unit Test</h3>
<p>When I am writing unit test, I often find myself stuck with the question, “What to check?” I usually got out by thinking inwardly and selfishly. What is the single concern for this function?</p>
<p>So in this case, I am not concerned with who is observing this command event. But I am concerned about:</p>
<ul>
<li><code class="language-text">command</code>’s value being set with <code class="language-text">todayDate</code>, if <code class="language-text">todayDate</code> is not null.</li>
<li><code class="language-text">command</code>’s <code class="language-text">setValue</code> is not called when <code class="language-text">todayDate</code> is null.</li>
</ul>
<p>Let us address the first concern, which is to be able to verify that <code class="language-text">command</code>’s <code class="language-text">setValue</code> is being called.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token annotation builtin">@Mock</span>
<span class="token keyword">lateinit</span> <span class="token keyword">var</span> mockProvider<span class="token operator">:</span> DependencyProvider
<span class="token annotation builtin">@Mock</span>
<span class="token keyword">lateinit</span> <span class="token keyword">var</span> mockCommand <span class="token operator">:</span> SingleLiveEvent<span class="token operator">&lt;</span>Command<span class="token operator">></span>
<span class="token annotation builtin">@Before</span>
<span class="token keyword">fun</span> <span class="token function">setUp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
MockitoAnnotations<span class="token punctuation">.</span><span class="token function">initMocks</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span>
<span class="token function">whenever</span><span class="token punctuation">(</span>mockProvider<span class="token punctuation">.</span><span class="token function">getCommmand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">)</span>
viewModel <span class="token operator">=</span> <span class="token function">MyViewModel</span><span class="token punctuation">(</span>mockProvider<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token annotation builtin">@Test</span>
<span class="token keyword">fun</span> <span class="token function">handleButtonClick_whenTodayDateNotNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">val</span> testDate <span class="token operator">=</span> <span class="token string">"2018-07-22"</span>
<span class="token comment">//Pretend here that some UseCase sets todayDate as testDate</span>
viewModel<span class="token punctuation">.</span>todayDate <span class="token operator">=</span> testDate
viewModel<span class="token punctuation">.</span><span class="token function">handleButtonClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">val</span> argumentCaptor <span class="token operator">:</span> ArgumentCaptor<span class="token operator">&lt;</span>Command<span class="token punctuation">.</span>LaunchNextActivity<span class="token operator">></span> <span class="token operator">=</span>
ArgumentCaptor<span class="token punctuation">.</span><span class="token function">forClass</span><span class="token punctuation">(</span>Command<span class="token punctuation">.</span>LaunchNextActivity<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span>argumentCaptor<span class="token punctuation">.</span><span class="token function">capture</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
Assert<span class="token punctuation">.</span><span class="token function">assertEquals</span><span class="token punctuation">(</span>testDate<span class="token punctuation">,</span> argumentCaptor<span class="token punctuation">.</span>value<span class="token punctuation">.</span>dateString<span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre></div>
<h3>Mocking Dependencies</h3>
<p>For us to verify <code class="language-text">command</code>’s function is being called, we need to mock it.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token annotation builtin">@Mock</span>
<span class="token keyword">lateinit</span> <span class="token keyword">var</span> mockCommand <span class="token operator">:</span> SingleLiveEvent<span class="token operator">&lt;</span>Command<span class="token operator">></span></code></pre></div>
<p>We want to override the <code class="language-text">provideCommand()</code> to get it to return <code class="language-text">mockCommand</code>, so we need to mock our Injector as well. This <code class="language-text">mockProvider</code> is passed into our ViewModel, see line 5.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token annotation builtin">@Mock</span>
<span class="token keyword">lateinit</span> <span class="token keyword">var</span> mockProvider<span class="token operator">:</span> DependencyProvider
<span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token function">whenever</span><span class="token punctuation">(</span>mockProvider<span class="token punctuation">.</span><span class="token function">getCommmand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">)</span></code></pre></div>
<p>All these mocks will be initialised when the following function is called.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin">MockitoAnnotations<span class="token punctuation">.</span><span class="token function">initMocks</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span></code></pre></div>
<p>There, we have bent the dependencies to our will, we can move on now.</p>
<h3>Verifying and Asserting</h3>
<p>Line 19. We can verify whether or not <code class="language-text">setValue</code> is being called by specifying <code class="language-text">any()</code>. This means we do not really care about the value of the parameter that is passed into the <code class="language-text">setValue</code> function.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token function">verify</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token function">any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre></div>
<p>So to check that it is not called,</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token function">verify</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token function">any</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre></div>
<p>However, we ARE concerned that the correct value is being used for <code class="language-text">setValue()</code>. Mockito’s ArgumentCaptor class do that.</p>
<p>They allow you to capture the variable that is being passed as an argument, for the function that you are verifying.</p>
<div class="gatsby-highlight" data-language="kotlin"><pre class="language-kotlin"><code class="language-kotlin"><span class="token function">verify</span><span class="token punctuation">(</span>mockCommand<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span>argumentCaptor<span class="token punctuation">.</span><span class="token function">capture</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
Assert<span class="token punctuation">.</span><span class="token function">assertEquals</span><span class="token punctuation">(</span>testDate<span class="token punctuation">,</span> argumentCaptor<span class="token punctuation">.</span>value<span class="token punctuation">.</span>dateString<span class="token punctuation">)</span></code></pre></div>
<p>After capturing, we can then perform simple assertions on the captured value.</p>
<h3>Summary</h3>
<p>Kotlin’s sealed class can be used effectively to send events with parameters to View classes, from ViewModels.</p>
<p>These sealed classes are wrapped with <code class="language-text">SingleLiveEvent</code> live data classes, so Views can create observers to observe changes in value.</p>
<p>We can use Mockito’s ArgumentCaptors to capture what is being used to set these live data, thereby verifying the correctness of the function.</p>
<p><em>Original post by Boon Keat at <a href="https://medium.com/@bkteoz/using-mockitos-argumentcaptor-on-singleliveevent-8c1aa1fadfbd">here</a>.</em></p></content:encoded></item><item><title><![CDATA[How to get your mobile app users to give you ratings and feedback!]]></title><description><![CDATA[TL;DR Does asking users for feedback and ratings help in your app? Yes!When I joined Singapore Press Holdings in late 2015, most of the apps…]]></description><link>https://sphtech.github.io/get-your-app-users-to-give-feedback/</link><guid isPermaLink="false">https://sphtech.github.io/get-your-app-users-to-give-feedback/</guid><pubDate>Sun, 07 Oct 2018 23:10:00 GMT</pubDate><content:encoded><p><strong>TL;DR</strong> Does asking users for feedback and ratings help in your app? Yes!</p>
<p>When I joined Singapore Press Holdings in late 2015, most of the apps published were receiving low ratings and bad reviews. As I was working in the Chinese media mobile application team, I proposed a method to collect reviews and encourage users to give ratings. We tested the implementation in the zaobao.sg app (the flagship Chinese language news and videos app).</p>
<p>After rolling out the new functionality, the reviews and ratings in the Google Play Store doubled. Most of the reviews received were very helpful for the team, as they shed light on how we could improve the product.</p>
<p>From the screenshot below, you could see that there was a huge surge of reviews, and ratings increased by 10% after the implementation was added in February 2017. The average number of reviews increased beyond 100%.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/4d42232e07032296408b0a75d8f9815e/8b3d9/zaobao-app-chart1.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 971px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 48.1977342945417%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABo0lEQVQoz2WRWW/TQBSF/c954kcgJErFA+ob8NBKhCVSaSsEArElJG3tZqkdjz2JZ+zZN+5MWoHEkT26tu935txx5pOcs7D4dAcfqGTHNxcvr8cv8ouPm5kzxqWvUsphGJzbQz7TWncdUUoj1HDOA8gHqfC8ev6rGq26dcso9IQkxhjA4V5Z8qNCYkKxMTaxwfWVRz9jZZzTVps7GEjghVI2JgkZvLLOaM2s0w7Sg4F3itJ+uVJaq3tZaxPcS6X+7lyjGmMshNTa7PtgMMDA6e5RKfAG0zYKN3V19Hnx4Hj26EOeQSsBUQJF+EdwdvsRQGjQiHLS7VqM2waNp7cH5/m7Ocogj4txwRoSG2h9Nh+flRMolIZtIYZkQgyCxyHgMsp7u7eNsA2euTiSSevDT4dPJ6+3QnFtwn/yKYwBCn6VNa7sFmf5qOPbTVd2TCxWj78WTw5/HJwUb98vT0eL0+/t9M3y/KQYf6m+HU1eTdFvqeOxZcqYpr3hXbHZrWtSlttbMeSKXqohJ/11Sy5rcrXaXlUkr/Cs7YtmN8X9mpt4QH8Ad7k0jshkD1kAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Taken from Zaobao Google Play Console"
title=""
src="/static/4d42232e07032296408b0a75d8f9815e/8b3d9/zaobao-app-chart1.png"
srcset="/static/4d42232e07032296408b0a75d8f9815e/eb41c/zaobao-app-chart1.png 293w,
/static/4d42232e07032296408b0a75d8f9815e/7110a/zaobao-app-chart1.png 585w,
/static/4d42232e07032296408b0a75d8f9815e/8b3d9/zaobao-app-chart1.png 971w"
sizes="(max-width: 971px) 100vw, 971px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Taken from Zaobao Google Play Console</figcaption>
</figure></p>
<p>Additionally, we also received feedback from anonymous users who did not wish to put their reviews in Google Play Store as well.</p>
<h3>How did we implement it?</h3>
<p>Before the implementation, we used to have a feedback page like this:</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/fb2cc810c6f4f3524b84ed5f64d6c217/039b6/zaobao-app-chart2.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 326px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 197.54601226993864%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,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'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="A screenshot is taken from Zaobao app"
title=""
src="/static/fb2cc810c6f4f3524b84ed5f64d6c217/039b6/zaobao-app-chart2.png"
srcset="/static/fb2cc810c6f4f3524b84ed5f64d6c217/eb41c/zaobao-app-chart2.png 293w,
/static/fb2cc810c6f4f3524b84ed5f64d6c217/039b6/zaobao-app-chart2.png 326w"
sizes="(max-width: 326px) 100vw, 326px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">A screenshot is taken from Zaobao app</figcaption>
</figure></p>
<p>While it did receive some responses from users, I felt that it was not significant enough. The users who sent feedback via the Settings page was either a hardcore user who love your app or probably a subscriber who was going to complain about something >.&#x3C;</p>
<h3>Be proactive. Prod the user for feedback in the app!</h3>
<p>We targeted users who had installed the app for over a certain period of time <strong>and</strong> consumed a lot of contents. We thought these users would be potential feedback providers. You would also have to think what are the best criteria to determine a potential user of your app, as it depends on the uses of your app.</p>
<p>In our case, since we were working on a news app, we expected the user to open and consume it on a daily basis. Also, some users may uninstall the app after a short period of usage. <em>(On that note, it would be helpful if Google Play Store allows app developers to collect users’ feedback upon un-installation)</em> Therefore we could only target users who continued to use the app. We wanted to know the reasons why they continued to use the app, and what motivations or goals made them continue to use the app etc.</p>
<p>Hence, we just asked: ”<strong>Enjoy the app?</strong>” (Enjoy my article? LoL)</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/555dd2a108db1df2087c6a836f018b19/51a3e/zaobao-app-chart3.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 72.14285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAYAAAAvxDzwAAAACXBIWXMAAAsTAAALEwEAmpwYAAADdElEQVQ4y02Uf0xVZRjH779stZJxL/fnOed9zzn3BxcQuCAm10HgNWHGD5W2bLVmW7k2cxVk4aZ/FekfjmotEfHKmjh1BrTo11obW3P+wYI7MloaiSE1xGbighru03v4EZ7tu+ec9zzns+c8z/d9XUIIHBmGgSVNYtIirAvMQAhLMwg7z2pdGCpHGKzmayo/akj6ErsZqm9hYlsL75Rsx+W8lCrZJzTqZCFdWWUcf3QjJwNVfJhVTIfL4pgrQkKzCDlAYwWoa0TDUS59/i2/T99kcn6WY50fPACUOnW+KH3uavpFLQOijh/bu5lMD/DdM29Rlq2hmcu5UiqgXyO+Oc7YrV8YuTVB39RlDr3/7gpQLFdYqxfQVbCF7kiKtJFi6NB7XO7s5cy+11hvm/hVhZoDU+CAqjBemc/wle+5OnWd6TszHD/RuQxc6qFuYIct8hJhYkJF1Ts720siy0N5jk5pwqYoJij2SRJ+kzxbJ688zvWJSWb/vM3i4n1O9/SsAYUahGEb6MUCXQ0jKHTikSinQjWc8dfQG0rRK1KcXlfBBU81rfFShC15Ze/LtO7bz8E3DrCrqUkBjTWgjErMfCUHrn4tT5eks5Oc99VwTksxUPIU/YXNfFq6k1djpWwORTmRvYmT/krSwWqOeh/DFTJ0VY2qKKjiJoNAWEVNVz3ViSngOXcVX8d3cvGRJJn9R1n4aZK54VE6KpvZvuUJbk/cYPbX37g39QejQ5dw7ZGlHPZtpM1fTptRxpuBMg56N/C6t4QCabPbn8/znnyec6sYKGKPKGOvXk6tO0Jjww7mFua5t/gv/wCjP4zhejtYwWD24/SFtvJlQTP9/hQXH05y9qEK4n5BjqGRqwaQG9Hx2BpuK0juep11EQ9PvlTPzPQUN4fHmB2/RmZkBFebO8Fn2jYu6CkGNzzNtZ4Bbpz/isEXWoiYPmTCwipSfVVyolVsYudZ+ANemp7dxcyVnxk90s3V7k8Yy2RwHYhVccrcykeW8l5iB1+0HuGbwx183PgihbaFFhbL205NflVSSLy5CljfyH3WrvHxcVym2qeW+sDZr2FhIt0+RI4XWw1JqjXHn/9ba0XSVECvl4bGBubuznH3zl8s/D1PxqnQORT0B2SoZEf6yoGxZHrlgtX7JaCUyhVBkskkXV1dS0qn07S3t/MfXHMlnLtnif8AAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Example flow we used (Words are translated into English for demo purpose)"
title=""
src="/static/555dd2a108db1df2087c6a836f018b19/1c968/zaobao-app-chart3.png"
srcset="/static/555dd2a108db1df2087c6a836f018b19/eb41c/zaobao-app-chart3.png 293w,
/static/555dd2a108db1df2087c6a836f018b19/7110a/zaobao-app-chart3.png 585w,
/static/555dd2a108db1df2087c6a836f018b19/1c968/zaobao-app-chart3.png 1170w,
/static/555dd2a108db1df2087c6a836f018b19/51a3e/zaobao-app-chart3.png 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Example flow we used (Words are translated into English for demo purpose)</figcaption>
</figure></p>
<p>Our prompt only had a ”<strong>Yes</strong>” and ”<strong>No</strong>” option, with no other way to dismiss the “annoying” question. Selecting an option will lead to the next dialog box:</p>
<p>If the user answered ”<strong>Yes</strong>”, we would ask them a favour to give the app a rating on the Google Play Store. Some helpful users did give some useful suggestions besides rating the app.</p>
<p>If the user answered ”<strong>No</strong>”, we would appeal for their feedback so that we could improve the app. This would be the opportunity you could receive helpful feedback from the user.</p>
<p>We found that this technique had helped us tremendously. You might have seen similar patterns and prompts in some apps in the market too. This could perhaps become the trend or <a href="https://www.urbandictionary.com/define.php?term=M.E.T.A">M.E.T.A</a> in harnessing user feedback for product improvements!</p>
<p>Hope our discovery will help in your application development journey :)</p>
<p><em>Original post by Winson Tan at <a href="https://medium.com/@winsontan520/how-to-get-more-feedback-and-ratings-from-your-mobile-app-users-f74145895471">here</a>.</em></p></content:encoded></item><item><title><![CDATA[Developers Gym by Thoughtworks - 14 July]]></title><description><![CDATA[This was the second Developers Gym session organized by Thoughtworks, of which we were honoured to play host to. The coding dojo, formulated…]]></description><link>https://sphtech.github.io/developers-gym-by-thoughtworks-14-july/</link><guid isPermaLink="false">https://sphtech.github.io/developers-gym-by-thoughtworks-14-july/</guid><pubDate>Wed, 25 Jul 2018 14:25:00 GMT</pubDate><content:encoded><p>This was the second Developers Gym session organized by Thoughtworks, of which we were honoured to play host to. The coding dojo, formulated and taught by Gordon Song from Thoughtworks, aimed to impart programmers with the skills of practicing Test Driven Development (TDD) in their software development projects.</p>
<p>To ensure that there is enough face-time between the instructor and the students, and for them to reap the maximum benefits through the session, the seating capacity is confined to 20 students only. Madhu and Ye Maw joined in this session.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/0acf2740e149ed4b8a4950a7927acb73/5fd2a/IMG_8075.jpg"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1170px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAVDyWEqHJ//EAB0QAAAFBQAAAAAAAAAAAAAAAAABAgMSBBETISL/2gAIAQEAAQUCOrCnoqy7BvdzK/8A/8QAFxEBAAMAAAAAAAAAAAAAAAAAARARMf/aAAgBAwEBPwGh2P/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AVf/xAAgEAACAQEJAAAAAAAAAAAAAAAAAREhAhASMTJBUWFx/9oACAEBAAY/AqZbDwOeStqPLtKkfbP/xAAcEAEAAgIDAQAAAAAAAAAAAAABABEhUTFBYZH/2gAIAQEAAT8hzOF8Lu4XsoSrdxurUdAqZ+Sg8TA6jduzif/aAAwDAQACAAMAAAAQbC//xAAYEQEAAwEAAAAAAAAAAAAAAAABABEhkf/aAAgBAwEBPxChFBgHJ//EABkRAQACAwAAAAAAAAAAAAAAAAEAETFBkf/aAAgBAgEBPxBUxK7vs//EABwQAQEAAgMBAQAAAAAAAAAAAAERACExQVFxof/aAAgBAQABPxDdCeB0HL49axLINZ0JR9V0YIHqKA+NwYFVOAvBcawgKai3X7je6D4HP//Z'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Michael advising some of the students on TDD."
title=""
src="/static/0acf2740e149ed4b8a4950a7927acb73/64a7b/IMG_8075.jpg"
srcset="/static/0acf2740e149ed4b8a4950a7927acb73/57443/IMG_8075.jpg 293w,
/static/0acf2740e149ed4b8a4950a7927acb73/bedd3/IMG_8075.jpg 585w,
/static/0acf2740e149ed4b8a4950a7927acb73/64a7b/IMG_8075.jpg 1170w,
/static/0acf2740e149ed4b8a4950a7927acb73/5fd2a/IMG_8075.jpg 1400w"
sizes="(max-width: 1170px) 100vw, 1170px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Michael advising some of the students on TDD.</figcaption>
</figure></p>
<p>The session was organized into the following segments:</p>
<ul>
<li>Theory teaching</li>
<li>Practical Part 1</li>
<li>Break</li>
<li>Sharing of what has been done for Part 1</li>
<li>Practical Part 2</li>
<li>Sharing of what has been done for Part 2</li>
<li>Conclusion</li>
</ul>
<p>The programmers were tasked to do pair programming, based on the programming challenge introduced by the instructor during the Theory segment. After part 1, the programmers were then asked to change their pair partners before commencing part 2. This was to facilitate the exchange of ideas among more people in the group.</p>
<p>Some of the students were so enthusiastic that spontaneous deep discussions on how they tackled the programming challenge sprung up during the breaks. Their passion in coding was infectious!</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/73cd5864326efe7cad3072cebc656eb1/0a4ca/enthu-coders.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 614px;"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 163.19218241042347%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,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'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;box-shadow:inset 0px 0px 0px 400px white;"
alt="Courtesy of Michael and Hui Qian"
title=""
src="/static/73cd5864326efe7cad3072cebc656eb1/0a4ca/enthu-coders.png"
srcset="/static/73cd5864326efe7cad3072cebc656eb1/eb41c/enthu-coders.png 293w,
/static/73cd5864326efe7cad3072cebc656eb1/7110a/enthu-coders.png 585w,
/static/73cd5864326efe7cad3072cebc656eb1/0a4ca/enthu-coders.png 614w"
sizes="(max-width: 614px) 100vw, 614px"
/>
</span>
</a>
<figcaption class="gatsby-resp-image-figcaption">Courtesy of Michael and Hui Qian</figcaption>
</figure></p>
<p>We observed that pair programming may or may not be appealing to programmers. It can be of an “acquired taste”, some perform better and some perform less if they were to practice pair programming. Also, part 1 of the practical segment may be a little short too (45 minutes), since new students would need time to set up their IDEs and get the unit testing framework working, as well as pick up the theories and understand the programming challenge too.</p>
<p><figure class="gatsby-resp-image-figure" style="">
<a
class="gatsby-resp-image-link"
href="/static/02e3c7b5b410b96e758f856f153a0136/5fd2a/IMG_8082.jpg"
style="display: block"
target="_blank"
rel="noopener"
>