#52 - Software Qualities for Quality Software - Marco Faella
“Seriously good software is not just software that works. It is not just software that satisfies its functional requirements, so it does the right thing, but it also does it in the right way.”
Marco Faella is an associate professor at the University of Naples Federico II and the author of “Seriously Good Software”. In this episode, Marco explained what he means by seriously good software, looking at software quality from multiple different perspectives. We then dived deep into several of those software qualities with some practical tips on how software engineers can improve their craft to produce high-quality software. Towards the end, we also touched on the concept of minimum viable code, why it is important to have an idea of what the ideal code looks like, while still being practical in finding the right compromise.
Listen out for:
- Career Journey - [00:05:00]
- Role of Education for Software Engineers - [00:07:18]
- Seriously Good Software - [00:12:02]
- Software Quality Quadrants - [00:17:15]
- Speed and Time Efficiency - [00:20:23]
- Space and Memory Efficiency - [00:24:10]
- Reliability Through Monitoring - [00:26:54]
- Invariants - [00:32:11]
- Reliability Through Testing - [00:33:43]
- Readability - [00:36:15]
- Reusability - [00:39:25]
- Thread-Safety - [00:41:17]
- Minimum Viable Code - [00:46:29]
- 3 Tech Lead Wisdom - [00:48:44]
_____
Marco Faella’s Bio
Marco Faella is an associate professor in the Department of Electrical Engineering and Information Technologies at the University of Naples Federico II in Italy. Besides his research on theoretical computer science, Marco is a passionate teacher and programmer. For the last 13 years he has been teaching classes on advanced programming and has published a Java certification manual and a video course on Java streams. More recently, Marco has released his book titled “Seriously Good Software” that teaches techniques for writing high quality software.
Follow Marco:
- LinkedIn – https://www.linkedin.com/in/marco-faella-8675574/
- Twitter – https://twitter.com/m_faella
- Website – http://wpage.unina.it/m.faella
Mentions & Links:
- 📚 Seriously Good Software –
https://www.manning.com/books/seriously-good-software
- 35% discount code for all products in all formats – podtechleadj21
- 📚 Clean Code: A Handbook of Agile Software Craftsmanship – https://amzn.to/3HkAvlP
- 📚 Code Complete: A Practical Handbook of Software Construction – https://amzn.to/3vnN4dy
- 📚 Refactoring: Improving the Design of Existing Code – https://amzn.to/3JXNz27
- Internal vs. External Qualities – https://livebook.manning.com/book/seriously-good-software/chapter-1/12
- Functional vs. Non-functional Qualities – https://livebook.manning.com/book/seriously-good-software/chapter-1/17
- Software Quality Quadrants - https://drek4537l1klr.cloudfront.net/faella/Figures/01fig02.jpg
- Reliability through Monitoring – https://livebook.manning.com/book/seriously-good-software/chapter-5/8
- Union-Find Tree – https://www.ics.uci.edu/~goodrich/teach/cs260P/notes/UnionFind.pdf
- Thread safety – https://en.wikipedia.org/wiki/Thread_safety
- Immutability – https://www.ctl.io/developers/blog/post/immutability
Each issue is hand illustrated and 100% content. Use the promo code “techlead” to get a 10% discount on your annual subscription. Visit businessagility.institute/emergence to get your edition and support the company supporting your podcast.
Role of Education for Software Engineers
-
I also believe that there are many exceptions, many cases where smart people have done great things without completing their education, or perhaps even without any formal education. I think that’s even rarer, that’s even less likely, but of course it can happen.
-
On the other hand, I think that the regular formal education path is just the safest and most likely way to get into the business, and into the industry, and progress into the industry, and start with the right footings. To start with the good foundation is the best way to thrive in the industry, and to be flexible, and ready to adapt to new and changing technologies.
-
I’d like to tell them to try to enjoy their time there during their education. To endure and try to enjoy even the more theoretical, or the stuff that looks not immediately applicable, because I think at least most of it is still going to be useful.
-
To think more clearly, to develop abstraction, your feeling for abstraction and structure, even topics like physics and math; Even if they’re not directly related to your future programming or software engineering job, they still help put you into the right framework, into the right mindset.
Seriously Good Software
-
The idea of the title, and the idea of the book, is that “Seriously Good Software” is not just software that works. That is not just software that satisfies its functional requirements, so it does the right thing, but it also does it in the right way. And of course there’s many different ways to measure how fit and how good your software is.
-
For example, you have a readability, which is a non-functional quality, because the end user is not aware of whether your software is readable or not. But it’s still a very important quality, because it affects maintainability. And according to how readable it was, it’s going to be easier or harder to fix it or modify it, to evolve it in general.
-
I think there needs to be more focus, both in education and in some industries, on nonfunctional qualities: Things that are not immediately visible outside, but are still very important for the future evolution of the software or for its performance.
-
As a junior programmer, I think you should first of all develop an awareness of the different ways you could be programming your little function or your little class or your large subsystem. And, of course, this comes naturally with your experience.
-
Cultivate this awareness that there’s thousand different ways to program even a little unit, even a small class, sometimes even a small function. If you just cultivate this awareness of the possibilities of the variety of ways, and you’ll get used to it, then maybe in that short time that you are given, you can do something better. You can evaluate different possibilities in your mind weekly. You’re not going to spend a long time doing that. If you’re used to it, the different possibilities just come naturally to your mind.
-
Junior programmers need to actively pursue this mindset. And then, even in your little space and in your little time that you’re given, you can do something better. You can evaluate different possibilities and pick the right balance.
-
The right balance between different qualities–balance within performance and readability and so on, and robustness and so on and so forth, and the right amount of testing, etc.–depends on the context. So you should be aware of the possibilities, and at the same time, aware of the context of your application. And depending on the type of product you’re making, you should emphasize one software quality over the others.
Software Quality Quadrants
-
The first one is internal versus external qualities, and this is the distinction between qualities that the end user can actually see, and these are called external qualities.
-
For example, functional correctness. So the program does what it’s supposed to do. It provides the right functionality. That’s an external quality.
-
And the time performance is mostly an external quality. So if your program takes one second or 10 seconds to perform its actions, that’s very visible to the user.
-
On the other hand, the distinction is not so clear cut, because if your program takes one millisecond or two milliseconds, you can argue that it’s visible or not, well, it depends. If it’s a user facing program, then it’s not actually visible because it’s exactly the same for the end user. On the other hand, if it’s a service that’s going to be used inside a larger program, it may become visible. That single action, the one versus two millisecond action, is going to be taken 1 million times in a row. Then, of course, it becomes visible.
-
-
The other way to distinguish software qualities is functional versus non-functional. Non-functional means how it performs the task. For example, how much memory it takes? How much network bandwidth it requires? Those are non-functional qualities.
- Readability, which I mentioned earlier, is one of the major non-functional qualities because it’s both internal and non-functional, but it’s still a very important quality.
-
I don’t think that in principle, any quadrant is more important than others. I just think it’s a useful mental tool to have.
-
In some education and some industries, they perhaps put too much weight on some of these quadrants, particularly they put too much weight on external and functional qualities, and too little weight on the others. Whereas I think, in principle, they’re all equally important.
Speed and Time Efficiency
-
Time efficiency, I think, is one of the qualities that all software engineers have in mind most of the time. It’s not one of those qualities where you really have to stress and to emphasize. It shows how some of those theoretical foundations that I was talking about earlier can really make a difference.
-
You go a hundred times faster if you know the right data structure.
-
I think that’s a great point to prove the usefulness of formal education where you learn some specialized data structures.
Space and Memory Efficiency
-
Space efficiency, even if it’s like a cousin to time efficiency, (is) treated completely different. Time efficiency is always on your mind as a developer, whereas space efficiency is really like a secondary or perhaps sometimes almost considered irrelevant.
-
Of course, we have a lot of cheap memory these days, both RAM and cheap persistent memory. So of course, it’s correct to give it less weight in general.
-
There’s two kinds of situations where you want to check your space requirements, your space occupancy. They’re quite obvious, which is when you have a lot of data, or when you have a little memory.
-
At the other end of the spectrum, you have plenty of applications with a lot of data. Which is not data analytics, as you may be thinking. It’s video games. You have to squeeze all of that information in your disk, and then in your RAM, and in your graphic unit. So that’s another big industry where memory requirements are a big thing; are quite important.
Reliability Through Monitoring
-
It’s kind of related to defensive programming. And it’s also related to the well-known design by contract methodology.
-
The idea is to start with the explicit contracts for your methods. The whole book is focused on small units of code. So that’s why I usually talk about a single class or even a single method, but then most of these notions can be generalized to larger architectures.
-
Design-by-contract applied to a single method means having a clear and explicit contract for that method. And by contract, I mean, what are the assumptions that the methods make on its caller? And what are the guarantees that the method ensures to its caller? These are also called the pre-condition and post-condition of this method.
-
-
You can then hook some of this contract into the code, basically as assertions. So assertions are well-known programming technique.
-
It’s very similar to an if-then-else, but with an additional ability that you can turn all these checks on or off at runtime. If something bad happens, then raise this exception. But in such a way that the runtime, you can choose whether to check these conditions during the execution or not.
-
This applies especially to safety critical software, where you really, really care about reliability. So in addition to testing; it’s not an alternative technique. In addition to the testing, you have these assertions at the beginning of each method. And if you really want to push it, you can also add checks at the end of a method, to check that the method actually did what it’s supposed to do.
-
-
I think they compliment each other. The idea of testing is to actively submit inputs to your program to see whether will it reacts according to specification. Monitoring, on the other hand, is just enriching your program with periodic checks. One is active, testing. The other one is passive. That’s why I call it monitoring, because it’s like adding a monitor on top of our program. The ideal would be to have both.
Invariants
-
If you look above one method, and you look at the whole class, most of the time, you can identify some properties that the class object should always have. So some properties of the data fields of your class that should always be true. If you can identify such a property, that’s called an invariant.
-
It means something that doesn’t change, or at least it’s not supposed to change. So it’s something that your assertions can monitor. They can check that this condition is all true.
Reliability Through Testing
- The standard and the easiest way is just to enforce certain coding practices. There are code reviews, and in-code reviews, you can include a review of the test cases. So you can basically enforce that each class should have its unit tests.
Readability
-
Readability is just that your code should be understandable with little effort by a different person or by yourself.
-
Because if you don’t look at (your own code) for a month and then you have to go back to it, you probably forgot the details. And so you yourself will benefit if you have written it in a readable manner. So thinking about humans reading it, and not only about the compiler reading it.
-
Writing more readable code will obviously decrease the effort to understand it in the future. That’s the immediate consequence. But the most important consequence is the medium and long-term consequences that your software becomes more maintainable. It becomes easier to spot bugs. It becomes easier to fix them. And especially, it becomes easier to fix it in a way that doesn’t create new problems elsewhere. Because you actually understand what was intended in that code.
Reusability
-
Reusability used to be a big selling point for object-oriented languages when they came out about 30 years ago. But these days, it’s a lot less emphasized. I think it probably was overemphasized at the beginning.
-
There is a cost even in making your code more general. It then becomes more complex, more abstract, and probably less efficient and less readable. But you get that single benefit of it being reusable.
-
You really have to find the right balance. So I think in general, you want most of your codes to be application specific, not reusable. You’re going to have a few reusable parts, and most of the code is going to be ad-hoc and custom and not reusable.
-
Pick the right amount of reusability, and make it into a useful general framework. But on the other hand, if you rely too much on these frameworks, or if you try to have all your code too general, you end up with a code base that’s too abstract and probably less optimized.
Thread-Safety
-
The first suggestion I make in the book that I think can be interesting is to establish a clear concurrency policy for your class.
-
If you know that your class is going to be used concurrently by different threads, so even before rushing to put in mutexes and synchronization primitives here and there, you should first establish a clear concurrency policy.
-
What’s a concurrency policy? It’s a rule which says which operations on this class can be performed in parallel with no synchronization because they affect different data, and which operations instead need to be serialized, so need to be synchronized because they may affect the same data.
-
-
On the other hand, a much more general suggestion is to avoid all these problems. Just use higher-level concurrency primitives that your language or your framework is going to offer.
-
We should try to avoid all that mess from all that synchronization mess and race condition risks, and just use what’s built in your language or in your frameworks.
-
In other words, know your standard library, know your framework, and try to stay as high as possible in the abstraction so that all these problems have been taken care for you, and have been tested, and you run a lot fewer risks in this way.
-
Having objects that you cannot modify once you’ve created them, that’s what immutability means. That’s a powerful tool to avoid race conditions, to avoid a lot of the nasty bugs of concurrency.
- On the other hand, you may have performance costs associated with the immutable data because sometimes the context of the application really requires you to change the state of some objects.
-
You have to balance that. So if you have a lot of concurrency, or if you have a little concurrency, but you are in a really safety-critical application where a bug could have serious, big consequences on either economic or money or perhaps health consequences, if you’re working in the health technology business, then go with immutability. Pay a little performance cost, learn about efficient immutable collections, immutable patterns, and you’ll be safer.
Minimum Viable Code
-
I think it’s wise to aim or to have a desire for the perfect code. But on the other hand, have a rough idea of what the ideal code would look like.
-
But then, of course, there’re practical matters. There’re deadlines. There’re customers. So, of course, you have to compromise. I think the more you gain experience, the easier for you to find the right compromise.
-
It’s great to strive for a compromise, but at the very least, you should be aware of how it could be improved. You didn’t have time. That’s too bad. That’s life. But it’s a great achievement if you do it. You are aware of the compromise you’ve done. How you would improve it if your boss or your customer gives you an extra week. You already know how to make it better.
3 Tech Lead Wisdom
-
Try to read great code.
-
The first place where you can find great code is in your standard library. Whatever your favorite language is, perhaps you can start from the things that you use the most in your standard library.
-
Go into the standard library and check out how they are implemented behind the scenes.
-
-
Look behind the scenes.
-
If you actually go and check how they (technology) are implemented, you’re going to use it with more awareness.
-
I’d like to emphasize awareness very much. You will ensure better quality software if you know what’s going on behind the scenes.
-
-
Value your users’ time.
-
Especially if you’re writing user-facing software, like applications or even web applications, your user time is extremely important.
-
Really value your user time, which means pay attention to time performance.
-
[00:02:10] Episode Introduction
[00:02:10] Henry Suryawirawan: Hello again, to all my listeners. Welcome back to another episode of the Tech Lead Journal podcast. Thank you for spending your time with me today listening to this episode. If you haven’t, please subscribe to Tech Lead Journal on your favorite podcast apps and also follow our social media channels on LinkedIn, Twitter, and Instagram. You can also make some contribution to the show and support the creation of this podcast by subscribing as a patron at techleadjournal.dev/patron, and help me to continue producing great content every week.
[00:02:41] Henry Suryawirawan: For today’s episode, I’m happy to share my conversation with Marco Faella. Marco is an associate professor at the University of Naples and the author of “Seriously Good Software”. As what the title says, this week, we are going to go back to the basics and discuss the important things that define good software quality.
In this episode, Marco explained what he means by seriously good software, looking at the software quality from multiple different perspectives, such as internal versus external qualities and functional versus non-functional qualities. We then dived deep into several of those software qualities with some practical tips on how software engineers can improve their craft in order to produce high-quality software. Some of those qualities that we discussed are time and space efficiency, reliability, readability. reusability, and thread-safety. Towards the end, we also touched on the concept of minimum viable code, why it is important for us to have an idea of what the ideal code should look like, while still being practical in finding the right compromise to produce good software.
I enjoyed going back to the basics and learning about the different software qualities from my conversation with Marco, and I hope that you will enjoy this episode as well. Consider helping the show by leaving it a rating or review on your podcast app, and you can also leave some comments on our social media channels. Though it may seem trivial, but those reviews and comments are one of the best ways to help me get this podcast to reach more listeners. And hopefully they can also benefit from all the contents in this podcast. Without further ado, so let’s get this episode started.
[00:04:19] Introduction
[00:04:19] Henry Suryawirawan: Hey, everyone. Welcome back to another episode of the Tech Lead Journal. So today I have with me a guest named Marco Faella. So he’s an associate professor of Computer Science at the University of Naples in Italy. He also does a lot of research in terms of Computer Science. He’s a passionate teacher and a programmer. He’s been teaching advanced programming class for the last 13 to 15 years, and he published quite a number of books as well. So one of it is “Seriously Good Software”, a book published by Manning Publisher. So Marco, it’s really a pleasure to have you in the show today. Looking forward to talk about all the programming stuff today.
[00:04:57] Marco Faella: Thank you. Thank you so much for having me.
[00:05:00] Career Journey
[00:05:00] Henry Suryawirawan: So Marco, maybe in the beginning for people who don’t know you yet, could you introduce yourself, maybe telling us more about your career journey, any kind of highlights and turning points in your career?
[00:05:10] Marco Faella: Yes. So, I was born here in Naples, in Southern Italy, and I did most of my studies here in University of Salermo, that’s in the South of Italy. So I did my Bachelor, Master and PhD there. During my PhD, I then moved to the US and spent a couple of years at the University of California, Santa Cruz, which is a great place. It’s a great town. It’s a great campus inside a forest. It’s really beautiful. I spent three to four years there, finishing my PhD and then as a postdoc. That was really a changing experience, a really powerful experience, probably the most productive time in my career. So I had a great time there, and then I applied for a permanent position back home in Italy. So I went back home in a way and became an assistant professor at University of Naples. That’s the biggest city in Southern Italy. I tried to visit the US from time to time whenever I can, obviously not this latest two or three years, but I try to visit whenever I can. And then I was promoted as an associate professor a couple of years ago. That’s pretty much my career.
Topic wise, my research is actually in theoretical computer science. So I live a double life academically speaking, because my research is theoretical, but on the other hand, the university asks me to teach practical programming topics. But on the other hand, I really like it. I really like both worlds, both the theory world and the practical programming world. So I really enjoyed teaching advanced programming for 15 years, and also a variety of different types of programming classes, like teaching low-level programming, C programming and low level operating system, Unix system core programming, and also some software engineering classes. So at a broad spectrum of programming topics. From this variety of topics, I came up with the book idea with the “Seriously Good Software” idea.
[00:07:18] Role of Education for Software Engineers
[00:07:18] Henry Suryawirawan: Thanks for sharing your journey. So, one thing I noticed is that you spend most of your life in the education industry, right? So becoming a professor, doing a lot of research, and also teaching a lot of students. So one thing that I always have in mind personally, is that in this modern era of software engineering, what is actually the role of education in your point of view? Because there are a lot of mixed opinions about this. Some people say you don’t actually need Computer Science to be a software engineer, but there are also people saying that, okay, you need some fundamentals that you can learn only through studies. So what’s your view on this being in the academic for quite a while?
[00:07:52] Marco Faella: So, of course, my view is going to be quite biased because my whole journey has been in academia as an educator. So I’m practically bound to think that education is really important, and I do think that. I’m aware of the discussion, and I also believe that there’re many exceptions, many cases where smart people have done great things without completing their education, or perhaps even without any formal education. I think that’s even rarer, that’s even less likely, but of course it can happen. On the other hand, I think that the regular formal education path is just the safest and most likely way to get into the business, and into the industry, and progress into the industry, and start with the right footings. To start with the good foundation is the best way to thrive in the industry, and to be flexible, and ready to adapt to new and changing technologies.
[00:08:59] Henry Suryawirawan: So in this sense, for example, a lot of people also saying the education curriculum, the things that we are taught in the university, for example, are probably not so much relevant in the modern days, where a lot of startups are thriving, and they use a lot of cool technologies, modern technologies. And somehow I think the curriculum for some universities probably do not catch up with that. So, in your view, what has changed in the last few years in academics? Can we now rely on university to produce a good quality software engineer?
[00:09:27] Marco Faella: That’s a tough question and I don’t have the full picture. I can talk about my own experience, and of course, that’s somewhat limited and perhaps limited to a special place or a particular place in Europe. So in my area, I think we do a pretty good job of teaching a good mixture of foundational and practical up-to-date topics. For example, we are in touch with local companies, with local IT companies, we send our students to. So we have agreements with local companies. Our Bachelor’s students spend a couple of months at the end of their degree, they spend a couple of months inside working there. So we are in touch with their supervisors, and we talk with them about their requirements, what technologies they use.
And of course, we don’t go all the way to teach specific frameworks because we think that’s not our job. But we try to design a path and educational path that goes in the right direction. So for instance, with the right programming languages, up to date with modern versions of those languages, a modern construct. For example, I usually teach Java. So I will update the curriculum to include the recent directions that Java is taking. Like more functional stuff, or the stream library, more concurrency. We introduced Android as a relatively newer operating system and ecosystem and stuff like that.
[00:11:05] Henry Suryawirawan: So since you have been a professor for quite a while, do you have any message for those students who are still studying? Maybe they’re undergrad, or maybe they’re post-grad, what message do you want to tell them to make use their time during the university?
[00:11:19] Marco Faella: Perhaps I’d like to tell them to try to enjoy their time there during their education. To endure and try to enjoy even the more theoretical, or the stuff that looks not immediately applicable, because I think at least most of it is still going to be useful. At the very least, to think more clearly, to develop abstraction, your feeling for abstraction and structure, even topics like physics and math. Even if they’re not directly related to your future programming or software engineering job, they still help putting you into the right framework, into the right mindset.
[00:12:02] Seriously Good Software
[00:12:02] Henry Suryawirawan: So let’s move on to your book itself, which is titled “Seriously Good Software”. The term “Seriously” here I think must mean something, right? So maybe you can tell us a little bit more what do you mean by Seriously Good Software?
[00:12:15] Marco Faella: So what I mean by that, even though the title was Manning’s idea, I was going for more something like “Exercises in Style”. Because the main idea of the book is to present the same small class, same small toy example in many different ways, optimizing for different software qualities. Maybe we’re going to talk about that later. So Manning came up with this title. It’s probably more catchy. I trust them. They’re great. They have a great marketing department, so I trust their feeling.
So the idea of the title, and the idea of the book, is that “Seriously Good Software” is not just software that works. That is not just software that satisfies its functional requirements, so it does the right thing, but it also does it in the right way. And of course there’s many different ways to measure how fit and how good your software is. For example, you have a readability. So, which is a non-functional quality because the end user is not aware of whether your software is readable or not. But it’s still a very important quality, because it affects maintainability. So software is certainly going to have bugs. It’s certainly going to need to be modified, extended. And according to how readable it was, it’s going to be easier or harder to fix it or modify it, to evolve it in general.
So that’s just an example of a nonfunctional quality that is often undervalued. So at least once again, from my educational point of view, we must be focused on functional qualities in education. But I think there needs to be more focus, both in education and in some industries, on nonfunctional qualities. Things that are not immediately visible outside, but are still very important for the future evolution of the software or for its performance.
[00:14:18] Henry Suryawirawan: So speaking about not just about functional requirements, and not just whether it works or not? Whether it gives you the right output? But there are also other qualities which probably we’ll discuss a little bit further. But obviously, there are so many software engineers or maybe not just software engineers sometimes, because of the demand from either like the business people or from the company to just produce software as fast as possible. What do you think we can do maybe as a profession or a software engineer, what we should do in terms of personal situation? If we are about to write this kind of software, how should we convey the message that actually the things that matter is not just about whether it’s working or not based on an input and output?
[00:14:58] Marco Faella: I think you can approach the problem at different levels. For example, if you’re a junior programmer, so then maybe you don’t have the power to fight. So you’re just given a deadline, and you have to do your best, and you cannot move the deadline. You probably cannot even argue for different objectives, different requirements. So what can you do as a junior programmer? As a junior programmer, I think you should first of all develop an awareness of the different ways you could be programming your little function or your little class or your large subsystem. And of course, this comes naturally with your experience, but maybe my book can give a little help there. Cultivate this awareness that there’s thousand different ways to program even a little unit, even a small class, sometimes even a small function. You could do that a thousand different ways, and maybe they all take the same time to you. So it’s not that you have to think for a month to find the best one, but if you just cultivate this awareness of the possibilities of the variety of ways, and you’ll get used to it, then maybe in that short time that you are given, you can do something better. You can evaluate different possibilities in your mind weekly. You’re not going to spend a long time doing that. If you’re used to it, the different possibilities just come naturally to your mind. And of course, experienced programmers, I think, they naturally go into that state. But junior programmers need to actively pursue this mindset, I would say. And then, even in your little space and in your little time that you’re given, you can do something better. You can evaluate different possibilities and pick the right balance. And let me also add that the right balance between different qualities, balance within performance and readability and so on, and robustness and so on and so forth, and the right amount of testing, etc. This right balance depends on the context. So you should be aware of the possibilities, and the same time, aware of the context of your application. And depending on the type of product you’re making, you should emphasize one software quality over the others.
[00:17:15] Software Quality Quadrants
[00:17:15] Henry Suryawirawan: So speaking about software quality, I know that you have this quadrant that you mentioned in the book, which is a quadrant about internal versus external qualities and also functional versus non-functional qualities. Maybe, can you give us a little bit of overview, what do you mean by all these different quadrants?
[00:17:30] Marco Faella: Yes. I have this tool, these two distinctions. Of course, they are classical software engineering category, so I didn’t come up with them. So the first one is internal versus external qualities, and this is the distinction between qualities that the end user can actually see, and these are called external qualities. So for example, correctness, of course, functional correctness. So the program does what it’s supposed to do. It provides the right functionality. That’s an external quality. And the time performance is mostly an external quality. So if your program takes one second or 10 seconds to perform its actions, that’s very visible to the user. On the other hand, so the distinction is not so clear cut, because if your program takes one millisecond or two milliseconds, you can argue that it’s visible or not, well, it depends. If it’s a user facing program, then it’s not actually visible because it’s exactly the same for the end user. On the other hand, if it’s a service that’s going to be used inside a larger program, it may become visible. That single action, the one versus two millisecond action, is going to be taken 1 million times in a row. Then, of course, it becomes visible. So, that’s the first distinction.
The other way to distinguish software qualities is functional versus non functional. Once again, functional means what the program does. Like correctness, does the right thing or not? And non functional means how it performs the task. For example, how much memory it takes? How much network bandwidth it requires? Those are non-functional qualities. Readability, which I mentioned earlier, is one of the major non-functional qualities because it’s both internal and non-functional, but it’s still a very important quality, as I was mentioning earlier.
[00:19:20] Henry Suryawirawan: So when you construct this quadrant, right, for example, you have maybe internal and functional, and internal non functional, and vice versa. So, in your view on this, is there any quadrant that is more important than the others? Or how should we read this quadrant? Because there are probably different qualities that reside in each of the quadrant.
[00:19:38] Marco Faella: I don’t think that in principle, any quadrant is more important than others. I just think it’s a useful mental tool to have. Also, I think that in some education and some industries, they perhaps put too much weight on some of these quadrants, particularly they put too much weight on external and functional qualities, and too little weight on the others. Whereas I think, in principle, they’re all equally important. Or if you want to be more precise and more specific in different contexts and in different industries, one quadrant maybe more important than others. But not in general, really depends on the context.
[00:20:23] Speed and Time Efficiency
[00:20:23] Henry Suryawirawan: So speaking about some of the important software qualities. I know you mentioned in the books, there are a couple of them. In fact, you break it down in different chapters, in which there are multiple exercises for each of the chapter that people can do, following the toy project that you mentioned earlier. So let’s go through maybe one by one. Starting with speed or time efficiency. I think most people probably understand this, but maybe can you describe further about speed and time efficiency?
[00:20:49] Marco Faella: Yes. So let me just take one minute to explain again the structure of the book. There’s this single toy example, which is a single class. It’s a water container example. You have a bunch of water containers, you can pour water into them and you can connect them. And that’s it. Very simple class, three methods. The unique idea about this book is to carry this example over and over with different software qualities and optimize it. Each time, optimize it with a different objective. And if it was for me, I would have done like a shorter book only on this example. But then Manning wisely suggested to add more examples and exercises, otherwise the book would have been too narrow, and I think they were probably right. And so, that’s not the only example in the book. So in each chapter, I tweaked the software quality. I show how that main example would be optimized for that quality. And then I talk about some other examples, and then as you said, there’s also exercises at the end when you can apply those optimizations to a different example, and you can check some solutions at the end.
And so I have a chapter for each seven software qualities. The first one is time efficiency, as you said. So time efficiency, I think, is one of the qualities that all software engineers have in mind most of the time. It’s not one of those qualities where you really have to stress and to emphasize. But still, I think it’s a very nice example, and it shows how some of those theoretical foundations that I was talking about earlier can really make a difference. So, for example, on this simple water container example that I have in the book, in this chapter I have different simple implementations with different data structures. I represent these water containers with sets, with lists, and I compare them from different points of view, particularly from a time performance. And if you check the book, you’ll see that if you use a more specialized data structure, which is based on trees, then you can gain up to two orders of magnitude speed in a typical use case on this water container. So you go a hundred times faster if you know the right data structure.
I think that’s a great point to prove the usefulness of formal education where you learn some specialized data structures. It may seem to you that you’re not going to use them. But then, as I show in the book, even such a simple example, where if you look at the specifications of this water containers, I think you would never guess that such a specialized data structure is called union-find trees, you would never guess that such a data structure would be so helpful in this case.
[00:23:30] Henry Suryawirawan: Speaking about data structure, I think, yeah, this is also one of the most fundamental theory that I think as a software engineer, we need to be exposed to as many as possible. Obviously, not all data structures are used frequently. So most probably people are familiar with List, Set, Map, dictionary. But yeah, I agree that so many software engineers probably rely too much on just these primitives. And in fact, different implementation of, for example, list also matters. For example, linked lists versus array lists and things like that. So I think, yeah, thanks for emphasizing that again, that actually just by implementing a correct data structure, you could gain a lot of, for example, time efficiency, right? Just by changing the implementation.
[00:24:10] Space and Memory Efficiency
[00:24:10] Henry Suryawirawan: So moving on to the second quality, which is about memory or space usage. This is also probably something less emphasized probably than speed. But I think it’s also important because these days, computer is so cheap. You can get a lot of RAM. You can put things on the cloud. So space sometimes is not really emphasized a lot. But maybe from your view, what should we care about in terms of space and memory?
[00:24:33] Marco Faella: Yeah, you’re absolutely right. So space efficiency, even if it’s like a cousin to time efficiency, it’s treated completely different. Time efficiency is always on your mind as a developer, whereas space efficiency is really like a secondary or perhaps sometimes almost considered irrelevant. And of course, as you say, we have a lot of cheap memory these days, both RAM and cheap persistent memory. So of course, it’s correct to give it less weight in general. On the other hand, basically, there’s two kinds of situations where you want to check your space requirements, your space occupancy. They’re quite obvious, which is when you have a lot of data, or when you have a little memory, and of course there’s still applications where you have little memory. For example, to the extreme of those would be embedded microcontrollers. So, embedded microcontrollers are everywhere. There’s tens or hundreds of them in your car. There’s tens of them in your toaster and microwave and so on and so forth. This hardware still has maybe 4 KB of memory. So if you’re going to program for that, really have to pay attention to your memory requirements.
At the other end of the spectrum, you have plenty of applications with a lot of data. Let me mention one such few that I find more or less familiar with, which is not data analytics, as you may be thinking. It’s video games. So video games have always had a lot of data. So, it’s a very competitive market. And the big titles, the so-called triple A, they really tried to push the graphics to the limit. And one way to push the graphics to the limit is to have a lot of data, a lot of textures, a lot of assets, as they’re called. You have to squeeze all of that information in your disk, and then in your RAM, and in your graphic unit. So that’s another big industry where memory requirements are a big thing, are quite important.
[00:26:32] Henry Suryawirawan: Interesting that you mentioned about video games. Because a lot of people actually think video games, all these data, they somehow do not see the importance of it. But actually as a video game developer, actually all you need to do is really optimize all these regarding space, regarding memory. And also bandwidth, especially if you play online games because you don’t want a jitter or lag in between your actions. So again, thanks for highlighting that.
[00:26:54] Reliability Through Monitoring
[00:26:54] Henry Suryawirawan: Moving on to the third quality, you mentioned about reliability through monitoring. So actually this is something that not so straightforward for me. Maybe, can you explain a little bit about that?
[00:27:03] Marco Faella: Yes. So I have two chapters on reliability. I think those are very important qualities. It’s so important that I decided to split it into two flavors. The second one, which maybe we’ll talk about later, is just testing, and that, of course, is very familiar to everyone of your listeners. The other flavor, which is perhaps less familiar, is what I called reliability through monitoring, and what I mean is it’s kind of related to defensive programming. And it’s also related to the well-known design by contract methodology. So let me briefly recall what I’m talking about here. The idea is to start with the explicit contracts for your methods. The whole book is focused on small units of code. So that’s why I usually talk about a single class or even a single method, but then most of these notions can be generalized to larger architectures.
Let me talk about small units. So, design-by-contract applied to a single method means having a clear and explicit contract for that method. And by contract, I mean, what are the assumptions that the methods makes on its caller? And what are the guarantees that the method ensures to its caller? These are also called the pre-condition and post-condition of this method. In the Java world, this is well established. In the JDK, for example, if you look at the official documentation of JDK, you will see that it’s expressed in a way which is pretty close to the design-by-contract methodology, because that’s the most natural thing to do. You say what the method expects from you. So, for example, what kind of arguments it’s supposed to receive, and also in what order you should call certain functions. Then what the method guarantees, what the method actually does.
And what I explained in this chapter is how you can then hook some of this contract into the code, basically as assertions. So assertions are well-known programming technique. Most programming languages have this abstract. It’s very similar to an if-then-else, but with an additional ability that you can turn all these checks on or off at runtime. That’s what an assertion is. If something bad happens, then raise this exception. But in such a way that the runtime, you can choose whether to check these conditions during the execution or not. The idea is that you should put all kinds of assertions in your program. So once again, it all depends on the context. This applies especially to safety critical software, where you really, really care about reliability. So in addition to testing, it’s not an alternative technique. In addition to the testing, you have these assertions at the beginning of each method. And if you really want to push it, you can also add checks at the end of a method, to check that the method actually did what it’s supposed to do. That’s the most extreme measure you can take. So you hook all these assertions in, and then during development, you leave these assertions on, and then once you have a good feeling that the program is correct, or few bugs are left, then you turn off these assertions because they’re going to cost time and space, and you don’t want that in production. So you turn them off. But they’re still there. They’re still there in the code. So as soon as your customer finds a new bug, you can turn them on, or you can instruct your customer to turn these assertions on. And these assertions will give you a more precise feedback on what kind of bug it was, on whether some of your code is violating the precondition of one of your functions, and so on.
[00:30:45] Henry Suryawirawan: A lot of people also actually approach this in a different way, maybe, it’s related to the second quality of reliability that you mentioned, is through testing, right? So all these inputs and outputs, some of the people probably put that as a test case instead of inside the method itself, which is what you called assertion inside the method. So what’s your take about this? Which approach that you think is maybe more relevant in one context over the other?
[00:31:08] Marco Faella: I think they compliment each other. So the idea of testing is to actively submit inputs to your program to see whether will it reacts according to specification. Monitoring, on the other hand, is just enriching your program with periodic checks. Okay. So they’re really complimentary to each other. One is active, testing. The other one is passive. That’s why I call it monitoring, because it’s like adding a monitor on top of our program and it’s not separate. Unfortunately, it’s mixed. It’s mixed in with your program. It’s the addition of these assertions in your code. But ideally, it would be like an added monitor that looks at your program while it’s running, and passively checks some conditions. So one is an active process. Testing is an active process, whereas monitoring and assertions are a passive observation kind of process. So they compliment each other. So the ideal would be to have both.
[00:32:11] Invariants
[00:32:11] Henry Suryawirawan: Thanks for clarifying that. So there’s another thing that you mentioned inside the design-by-contract, which is the characteristics of invariants. Can you maybe explain what is invariant for people who want to design their method properly?
[00:32:25] Marco Faella: Yes. So earlier I was talking about pre-condition and post-condition, so that’s the contract of a method. And then if you look above one method, and you look at the whole class, most of the time, you can identify some properties that the class object should always have. So some properties of the data fields of your class that should always be true. If you can identify such a property, that’s called an invariant. It means something that doesn’t change, or at least it’s not supposed to change. So it’s something that your assertions can monitor. They can check that this condition is all true. For example, that an integer field should never be negative. So in Java, you don’t have an unsigned integer type. Integers can always be negative, but very often, your integer fields should never be negative. That’s a very simple invariant. So you can check these invariant before and after each method. If you really want to implement more hard monitoring or complete monitoring, you can check that before and after its method, that field is always non negative. As soon as it gets negative, you will basically know who is the culprit, where the bug is. Because the first assertion will raise an exception, and you will know where the bug is.
[00:33:43] Reliability Through Testing
[00:33:43] Henry Suryawirawan: So moving on to reliability through testing. So testing is also a big topic in software engineering these days. And in fact, there are approaches, like test driven development, quality engineers, quality assurance. Anything that you want to emphasize here in terms of reliability through testing?
[00:34:00] Marco Faella: I don’t have much to add because I know it’s a very big topic. As you said, there’s big discussions. At one extreme you have test driven development, and then there’s some skepticism, especially in recent years, maybe this has gone too far. The emphasis on testing is perhaps too much. I have to say I don’t have a strong opinion about that. I leave this discussion to industry insiders. I don’t have the practical experience to have a solid opinion on this. So in the book, I just show a couple of basic techniques to design a test plan for the class. And then compare it with the other technique, which is the monitoring technique.
To be honest, of course, it’s a 200-page book, and as you said, testing is a big topic, but time performance is at least as big a topic. So all of the topics that I’m talking about in the book are very large, and they have an extensive literature. And so, in every chapter, I had to do a very strict selection of the few interesting topics from each of these areas. That’s why at the end of the chapter, I have a small section with suggested reading with more books and learning material to learn more about that topic.
[00:35:15] Henry Suryawirawan: So maybe a little bit deeper about software testing, because there are so many software engineers this day, I notice in the industries that sometimes these engineers do not emphasize really when they start writing their class, their method, they don’t actually think what kind of test cases that they should do. What they emphasize is on maybe the UI part, or maybe the API, if they’re writing some kind of APIs. So in your view, what should we do as a software engineer to actually in inculcate this practice that whenever you write a class, maybe you should think about what kind of test cases that you should put in mind?
[00:35:46] Marco Faella: Yeah. Well, of course, the standard and the easiest way is just to enforce certain coding practices. There’re code reviews, and in code reviews, you can include review of the test cases. So you can basically enforce that each class should have its unit tests. If it’s appropriate to your system, to your kind of software, and then review those test cases in the code reviews that everyone I think in the industry is doing.
[00:36:15] Readability
[00:36:15] Henry Suryawirawan: Right. So next is actually readability. So I think for some people, readability is a big thing. For some, I don’t really care. I just want to write lines over lines of code as long as it works. So what kind of balance in between that you have in mind when you write this readability chapter? Is this something that the code is what they say is beautiful like a narrative, when you read it as a prose? Or is it something different?
[00:36:40] Marco Faella: Well, in its basic form, readability is just that your code should be understandable with little effort by a different person or by yourself. You know, when a little time has passed since you wrote it. Because as your listeners know, even your own code, if you don’t look at it for a month and then you have to go back to it, you probably forgot the details. And so you yourself will benefit if you have written it in a readable manner. So thinking about humans reading it, and not only about the compiler reading it. That’s the basic idea.
As I was saying at the beginning of the interview, writing more readable code will obviously decrease the effort to understand it in the future. That’s the immediate consequence. But the most important consequence is that the long-term or medium and long-term consequences that your software becomes more maintainable. It becomes easier to spot bugs. It becomes easier to fix them. And especially, it becomes easier to fix it in a way that doesn’t create new problems elsewhere. Because you actually understand what was intended in that code. So even if there’s a bug, you can fix it with less fear of creating new problems elsewhere. There’s great resources out there for readability which I’ve based this chapter upon. The most important ones, two classic books are Clean Code by Martin and Code Complete. Those were my references. I think there’s a lot of great information there. Well, let me mention also a third classic which is related to readability, which is the Refactoring book. So in this chapter, I summarized some of the most important guidelines, and especially having my toy example as a reference, because I liked this idea of going back to the same example. So you can more easily appreciate the difference between different objectives. The trade-offs between different form of the objectives.
[00:38:41] Henry Suryawirawan: So I think one thing I noticed in my experience, you will know about readability once you look back to your code and read it. So you will understand, okay, is this code requiring a lot of a mental effort to actually understand what it does. Sometimes also a method that has a long number of lines. I think it’s also not so readable because you need a lot of things to put inside your context.
[00:39:02] Marco Faella: Some of the guidelines are really easy to learn, like variable names. The right names, the variables to function, keeping functions short. As you were saying, it just needs a little diligence and effort to apply them, but the rules are very, very easy.
[00:39:17] Henry Suryawirawan: And I must add that maybe expertise of using the IDEs, because some of these IDEs actually allow you to do some kind of shortcut where you can change your code instantly.
[00:39:25] Reusability
[00:39:25] Henry Suryawirawan: So related maybe to readability is the reusability concept. In a sense that you want your software, your function, your method to be reusable. But I understand as well, there are balance here. Like you probably don’t want everything to be reusable. Otherwise, maybe your software becomes too complicated. So what’s your view on this reusability?
[00:39:45] Marco Faella: Yes. So reusability used to be a big selling point for object-oriented languages when they came out. But these days, it’s a lot less emphasized. I think it probably was overemphasized at the beginning. Because as you said, there is a cost even in making your code more general. It then becomes more complex, more abstract, and probably less efficient and less readable. But you get that single benefit of it being reusable.
As you were saying, you really have to find the right balance. So I think in general, you want most of your codes to be application specific, not reusable. You’re going to have a few reusable parts, and most of the codes is going to be ad-hoc and custom and not reusable, and this is also connected to the issue of frameworks. So frameworks that are everywhere tend to be very general, like a framework like Spring. Those big frameworks, they are like the best part of reusability. They manage to give general services. They’re very successful, so they must’ve done something. Pick the right amount of reusability, and make it into a useful general framework. But on the other hand, if you rely too much on these frameworks, or if you try to have all your code too general, you end up with a code base that’s too abstract and probably less optimized, of course.
[00:41:17] Thread-Safety
[00:41:17] Henry Suryawirawan: Probably the last quality that we are going to go through here is about thread-safety. I think this is also one source of sometimes flakiness or race condition in some of the bugs that are popular. Whenever in your career, you find software that sometimes works, sometimes doesn’t work, probably it’s related to thread -safety. So what’s your view on this? What should engineer be mindful about whenever they write software about thread-safety?
[00:41:41] Marco Faella: Yes. The first suggestion I make in the book that I think can be interesting to your listeners is to establish a clear concurrency policy for your class. Once again, I like to talk about small units of code, because that’s easier to explain, to understand. You should generalize the larger systems, but take a single class. If you know that your class is going to be used concurrently by different threads, so even before rushing to put in mutexes and synchronization primitives here and there, you should first establish a clear concurrency policy. And what’s a concurrency policy? It’s a rule which says which operation on this class can be performed in parallel with no synchronization because they affect different data, and which operations instead need to be serialized, so need to be synchronized because they may affect the same data. So think about this for a second, for some classes it’s going to be obvious. Because objects will be unrelated. So everything that you do on different objects can go in parallel, and everything you do on the same object must be serialized. That’s a very common case.
But in other cases, including the simple, the apparently simple water container example, objects can be connected with each other. They can form like a graph. They have pointers to each other. And then the situation becomes a lot more complicated, and you really have to think about your concurrency policy. And only then you can check whether the concurrency policy that you were aiming at is actually implementable, and how to implement it and so on and so forth. This, of course, is for those unfortunate cases where you are programming a low level and you actually have to worry about synchronization and thread safety yourself. On the other hand, a much more general suggestion is to avoid all these problems. Just use higher-level concurrency primitives that your language or your framework is going to offer.
So first of all, we should try to avoid all that mess from all that synchronization mess and race condition risks, and just use what’s built-in in your language or in your frameworks. For example, in Java, you have a thread-safe collections. So those collections take care of synchronization. You don’t have to worry about that. Above that, in Java you have parallel streams since Java 8. So parallel streams are higher-level kind of collections where you can perform, for example, with a single call in the standard library, you have parallel sorting algorithm on the list. So you don’t have to code anything, just run this parallel sort. So in other words, know your standard library, know your framework, and try to stay as high as possible in the abstraction so that all these problems have been taken care for you, and have been tested, and you run a lot more fewer risks in this way.
[00:44:38] Henry Suryawirawan: There’s also another convention about this, which I read in some books as well, which is related to immutability concept. And that’s also one of the big reason why a lot of people actually suggesting about functional programming, where actually you have a lot of immutability embedded inside the construct, so that you can avoid this concurrency problem. Any thought from you about this?
[00:44:59] Marco Faella: Yeah, so having objects that you cannot modify once you’ve created them, that’s what immutability means. That’s a powerful tool to avoid race conditions, to avoid a lot of the nasty bugs of concurrency. I think that’s a great direction. For example, once again in Java, the Java strings work best with immutable data. On the other hand, you may have performance costs associated with the immutable data because sometimes the context of the application really requires you to change state of some objects. But if you’re entirely into an immutable system, every time you want to change something, you just have to create a new object that looks pretty much like the old one, except for a little change. And of course, this has a performance cost over the standard imperative way, which is to change that thing on the fly.
So you have to balance that. So probably, if you have a lot of concurrency, or if you have a little concurrency, but you are in a really safety critical application where a bug could have serious, big consequences on either economic consequence, or money consequences, or perhaps health consequences, if you’re working in the health technology business, then go with immutability. Pay a little performance cost, learn about efficient immutable collections, immutable patterns, and you’ll be safer.
[00:46:29] Minimum Viable Code
[00:46:29] Henry Suryawirawan: My last question on this topic, right? So as I’m relating my experience working in startups, and especially we know this concept about MVP, Minimum Viable Product. Is there such thing as a Minimum Viable Code, where you have enough quality in place so that you don’t over analyze and make your code perfect, so to speak. Is there such thing in your view about Minimum Viable Code?
[00:46:50] Marco Faella: Yeah, absolutely. So all of this, everything we discussed, those are mental tools. Those are aspirations. Those are aids. You should be your goals. So I think it’s wise to aim or to have a desire for the perfect code. But on the other hand, have a rough idea of what the ideal code would look like. That’s the first step. But then, of course, there’s practical matters. There’s deadlines. There’s customers. So, of course, you have to compromise. I think the more you gain experience, the easier for you to find the right compromise.
As I was saying at the beginning, if I have just 24 hours to deliver something, what’s the best trade-off I can achieve in 24 hours? Whereas an inexperienced programmer, in 24 hours, will just write whatever comes. I’m in a rush, so I would just write whatever comes to my mind. If it works, I’ll be done. A more experienced and more aware programmer, if he or she has a little time, would still consider different options, and go with the good enough solution. And even at the end of this short time, 24 hours, he or she will know what are the pitfalls? What are the limitations of his or her code? That’s great. I mean, it’s great to strive for a compromise, but at the very least, you should be aware of how it could be improved. You didn’t have time. That’s too bad. That’s life. But it’s a great achievement if you do it. You are aware of the compromise you’ve done. How you would improve it if your boss or your customer gives you an extra week. You already know how to make it better.
[00:48:32] Henry Suryawirawan: So thanks for your tips. Actually, knowing the pitfalls of the work that you just did. So I think it’s also an important thing. Especially, creating an awareness of that pitfall as well for other people. Because sometimes your code is taken over by others.
[00:48:44] 3 Tech Lead Wisdom
[00:48:44] Henry Suryawirawan: So Marco, it has been a pleasure to talk to you about all these software qualities. I relearned a lot of things myself, the fundamentals of software engineering. But before I let you go, normally I have this one last question that I ask for all my guests, which is to share about 3 technical leadership wisdom. So, Marco, would you be able to share your 3 technical leadership wisdom?
[00:49:05] Marco Faella: Yes, I will. As my book, they are intended for beginner to intermediate programmers. So they’re going to be quite natural to experts They’ve probably already practiced this. The first one is a double suggestion, which is first of all, try to read great code. So go out and look for great code. It’s very easy to find. The first place where you can find great code is in your standard library. So meaning, whatever your favorite language is, perhaps you can start from the things that you use the most in your standard library. Maybe you use lists all the time. Maybe you probably use strings all the time. Go into the standard library and check out how they are implemented behind the scenes.
That’s connected to the second thing. So if you do this, not only, you will read great code, but at the same time, you will be looking behind the scenes of the technology that you’re using. As another example, perhaps you’re a Python person. Check out how Python lists are implemented because Python is a very high level language and needs a lot of power, a lot of built-in data structures. But if you actually go and check how they’re implemented, you’re going to use it with more awareness. Once again, I’d like to emphasize awareness very much. You will ensure better quality software if you know what’s going on behind the scenes.
Finally, third and final tip. This is connected to time performance we were talking about earlier, which is value your user’s time. Especially if you’re writing user-facing software, like applications or even web applications, your user time is extremely important. Like I’m on Twitter and sometime ago I was reading someone complaining a very simple thing that in Windows, if you right click on a file, sometimes it takes one second, or maybe even a little more than one second to get that little menu. And I think he was absolutely right. It’s crazy. It’s a little delayed, right? People would say it’s just a second. I totally disagree with this. It’s absolutely right to complain that on a 8 core 4 GHz machine with 16 GB of RAM, a human user should wait one second to get that menu. Perhaps things have improved since then. I don’t want to be unfair to Windows, but I witnessed this myself a couple of times. So really value your user time, which means pay attention to time performance.
[00:51:37] Henry Suryawirawan: Wow. Yeah. So I could relate to that. So thanks for reminding us about valuing our user’s side. Because sometimes, software engineers, we just rush finishing the requirements. That’s it. Like we don’t really actually use the system. So that’s also another thing, right? We don’t use our system. Definitely, we don’t value their time, because we never use it.
Thanks again, Marco, for this pleasant conversation. If people want to connect with you, maybe talk more about these stuffs online, where can they find you?
[00:52:02] Marco Faella: I think Twitter is the best channel. Maybe I can share and put it on your website.
[00:52:07] Henry Suryawirawan: Okay. I will do so. So thanks again, Marco. Thanks for attending this conversation with me.
[00:52:12] Marco Faella: Thank you very much.
– End –