 First, and for the late-comers, I think they can still join us when they are ready. Okay, cool. Thanks, everyone, for coming. This is the second session of our developer gym. Michael, by the way, is recording. Okay, now, cool. So for this session, we will continue from our last session where I give you an introduction of what TDD is. And this session, we will do some coding cutter with the TDD skills. I saw some of the new faces in this session. So I will start with some of the kind of recap of what the TDD is. But it would be very brief. And the majority of this afternoon, you will spend by doing more coding and practice the skill by yourself. And actually, sorry, not by yourself, with your friends. So what is the code cutter? Code cutter is basically the sessions where you practice your basic programming skills like writing a simple program, writing test cases, make it run faster, make it run better. Basically, those basic skills, you are practicing again and again so that you will become a more proficient developer. So just similar to those Kung Fu masters, right? They become mastered by practicing those basic moves again and again. Those may look very boring if you just look at it by yourself. But in the end, it's those basic exercises and practices that make them Kung Fu master when they fight. Today, let's do some TDD practice or the exercise on the TDD. Let's do a quick recap. What is TDD? By the way, just to have a show of hands, how many of you are totally new to TDD? You are OK. And assume the rest. OK. Cool. But I will still give you some quick introduction what it is. So TDD or test-driven developments is our approach. It's approach or it's a style for you to write your code. So typically, when you develop your program, you will just write the logic to solve your problem. And you may or may not write test cases or sometimes you write test cases after you write your program. But then TDD is a kind of approach which encourages you or forces you to write the test cases in advance even before you write the first line of the code to solve your problem. The benefit is this test cases will help you to guide your design of your solution. As we will see later today, you will do some exercise and you will find out that those test cases help you to get focused on the step that you are moving and to help you to design your solutions. And also, in the end, if you practice this, you will accumulate a set of test cases which can be a safety net. Later, when you make further modifications to the system, et cetera, those test cases would be a layer of production for you. So there are typically three steps. If you follow the TDD style in writing your code, the first step is to write a test case, which obviously would fail in the beginning because you haven't got any logic or real implementation to make it a pass. The second step, you need to write the code, the production code, which makes this test case pass. And the secret here is you should just make enough implementation to make it a pass, no more than that. So for, you know, you should not think about error scenarios that is not being tested yet. You should not think about what will happen tomorrow or the day after tomorrow. You should not think about too much about the future. Just focus on the problem or the test case that is failing right now and make just enough changes in your code to make the test case pass. And then the third step, which is also very important, to do the refactoring on your code base. Later, I have another slide to talk about what kind of a refactoring you need to do. There are only four simple rules you need to follow today. And then after you make the code cleaner, then you finish one cycle, and then you can continue with the next cycle. Writing the next test case, make it a pass, and then make your code better. So just keep doing this kind of cycle. Okay, in the last session, we gave you a few rules or tips you should follow. So as beginners, I would suggest you to treat this as rules and basically to force yourself to follow those basic rules. And once you become more proficient, you might want to move in bigger steps, or maybe you can skip some of the rules. But in the beginning, try to follow them. So the first rule, okay, this rule you can't really violate the rule. This is TDD, it requires you to write the test case first, right? Before you write any production code, the first thing you should do is write a test case, which will fail in the beginning. This is the rule number one. Rule number two, you should only have one failing test case at a time. So some of the people, they may have a habit of writing a few test cases, which are all failing, and then they started to implement the code or the logic. That is, I would say it's a bad habit. So for TDD, you should really just follow the habit of having one failure test case, and then move on to the implementation to make this test case pass, then you have your second failed test case. So do not have more than one failed test case at a time. Again, the reason is because each test case is supposed to be a guide, right? You follow this failed test case, which makes you focused to just think about how to make it pass. If you have more than one failed test cases, your mind will be more kind of bloated, right? You're thinking about, what do I need to do in order to make those two test case pass or 10 test case pass, then you couldn't really get focused. So in order for you to get focused, just stay with one failed test case each time. Okay, the number three, you should write small test. Small meaning I have slide basically to describe what could be a small test. So one way to describe the small test is each test should only focus on one particular behavior or feature or maybe one rule or one pattern in the problem that you're trying to solve. And in terms of the time taken to make the test case pass, it should be very fast. So I put the tip number here like 10 minutes. But up to you, if the problem is very simple, maybe you should even achieve even shorter time to make the test case pass. But in reality, if you're solving a very complicated problem, maybe you can allow yourself to take, let's say, 20 minutes or so to make the test case pass. But still, the guideline is if your test case is small enough, you should not spend a lot of time to make it a pass. And typically, each test case should only have one reason to fail. And in some cases, this basically means that each test case, you should only have one assertion. If you're familiar with the unit test or those libraries, right, you know that you can use expect or assert, whatever. So you should try to have one assertion per test case. Then basically that means your test case only have one reason to fail. Number four, you should make the baby steps, meaning that you should write the production code to make the field test case to pass, no more than that, which is the one I already mentioned this one to you just now. Rule number five, so when you have a choice, right, the test case failed and when you try to make this test case pass, you may have a few choices. And among those choices, I would recommend you to pick the simplest one. To start with the simplest one first, and when you add more and more test cases, naturally your solution or your algorithm will grow more and more complicated. So do not run into the most generic solution or most complicated algorithm right away. Try to follow the simplest solution you can have. And then generalize your solution when you add more and more test cases. Rule number six, do not forget about refactoring. So remember there are three steps in the TDD cycle. And once you make the test case pass, the next step is always do the refactoring. Do not skip right away to your next test case. Look at your code. Are there anything you can improve? Are there any duplications in your code that you can simplify? Or are you choosing a more complicated algorithm? Can you simplify it? So think about along those directions to make your code better. Also do not forget to refact your test case. So once you have more and more test cases, you should also look into the duplications in your test cases. Are there anything you can make your test case more concise or more easier to understand, et cetera? So the test case in the end is kind of a live documentation for the code. If you make this test case easy to understand, whenever the test case fail, we have a better or have an easier life to troubleshoot why it fails. If the test case itself is very hard to understand, then in the end it becomes pretty useless. Rule number eight. So try not to do the refactoring when your test cases are failing. So this is kind of a pitfall. When you have failed the test case, you're trying to make some changes in your code to make the test case pass. But then you notice that some part of your code is ugly. And you feel that, oh, I should make this path better. And then at that time tell yourself, this is not my focus right now. My focus is to do something to make the test case pass. Even if there's some problem in this piece of code here, I should not worry about it right now. I should probably note it down somewhere. And then later, once you make the test case pass, there is a dedicated slot called refactoring for you to do the improvement at that time. So try not to mix the refactoring together with the second step, the implementation part. Okay, now let's talk about how do you do refactoring. So the term refactoring is referring to the changes you make in your code to make it better without changing the behavior. So it sounds very abstract. But then in the end there are four simple rules you need to follow or you can follow to make your code better. The first rule is your code needs to pass all the test case. This is rule number one. All right? Second one is your code needs to be clear enough to express your intention. So basically in the layman's term, that basically means that if you ask someone to read your code, he should be able to understand your code maybe in a few seconds without spending minutes or even hours to read and understand or even ask you, why? Why do you write in this part? What does it mean? So if someone needs to ask you what it means, then it's not clear enough. So you should try to write your code to express your intention so that it's so clear that the code explains by itself and nobody needs to ask around why what's the reason for this piece of code. Then the third rule is there shouldn't be duplications in your code. Whenever you see the code, two pieces of code, they look very similar, then you should try to do something to protect a common function or common methods to make it more reusable and avoid duplications in your code. The fourth rule, which is called minimal. So this rule takes a bit of time to understand and practice. So to make it easy to start with, you should just ask yourself, given a line of code, if I delete this line, would my code still work? If yes, just delete it. Or from the method or function level, I have a function here. Do I still need this function? If I delete this function, my code still works, then I should delete it. And from the, even on the higher level, let's say in your code you have a few classes, or let's say you have a few interfaces you define for some of the classes. Ask yourself, if I delete that interface, would my code still work? And if it doesn't say yes, then you should delete that interface. So basically try to go minimal. Write as little code as you need in order to solve the problem. So if you follow this direction, your code overall will become more simpler. So just remember this for lose to rewrite the code. Later during the exercise today, we will be following the TDD cycle. And during the refactoring, you should follow this for lose. Whenever you tell yourself, I'm going to do the refactoring now, then you should check your code. Does it pass all the test case? Or does it express your intention? And you will probably pair with some of your friends. And then just ask your friend, is this code clear to you? If it's not clear, then you need to refactor it to make the intention clearer. And then check, are there any duplications? Even your test cases, are there any duplications in your test case? What can you do to remove those duplications? Then are there anything you can delete? So ask yourself those questions. So to just check if your code can be further simplified. Okay, that's a quick crash course for what is TDD. And your last session, we spent a lot of time on theory. And I heard some people complain that the pace was a bit slow. So today, let's do more hands-on exercise. So you will hopefully enjoy this TDD style and learn something useful out of this. Today, the exercise we are going to do is write a program to convert Arabic numbers into Roman numerals. How many of you have heard about this exercise before? Okay. Number one, this is actually a pretty simple problem. So let's basically talk about the Roman numerals first. So you've probably seen some Roman numerals numbers before, so do you know the rules that form those numbers? If not, let's start with those rules first. So your task today, firstly, is to understand the rules. How do you form Roman numerals numbers? And later, your task is to write your code to convert the integers, the Arabic numbers into this Roman numerals form. So let's start with the Roman numerals rules. There are letters, right? Those funny letters, they have some meanings, like i stands for one and v stands for five and x stands for 10, et cetera. So there are a bunch of letters and each of the letters corresponding to one value. You can start with this first, okay? And then once you have those letters, you can combine them to make bigger numbers. For example, if you want to say two, two basically means one plus one, right? And that means you have an i, then followed by another i. Put them together, that basically means two. And then three is also very simpler. Three basically means one plus one plus one. So you just need to have three i's, put them together, that is three. Okay, so far the rules are pretty simple, but then the next one would be a bit more complicated. So the number four, it shows as r, v, the y. The rule here is v stands for five, i stands for one. So whenever you see i, which is one, before v, which is five, the i is smaller than five. In this case, it's not a summation, it's actually a subtraction. Meaning that in order to interpret this value, it is actually the value on the right minus the values on the left, which is five minus one, which is four. So this is the rule. Somehow they define it in this way, don't ask me why. So they probably feel this is funny, or maybe this is more efficient, but they design it in this way. So four is equal to five minus one, so you just need to put one in front of five, and you have four. And six, how do you express six? Six, okay, back to the normal rule. Six equal to five plus one. And then basically you have a v followed by i. So you ask me, okay, i should also, six is also equal to three plus three. Can you just put six i's there? The answer is no. So there are certain rules saying that those values, like i, maybe x, each of them, they can only be repeated maximally three times. You cannot repeat them more than three times. That's why for four, you can't really just put i, i, i, i. That doesn't work. That violates the rule. So that's why you need to express four as five minus one. That's why you have that i, v representation. And then for seven, it becomes easier again. Seven basically means five plus two, right? You have v plus i plus i. And eight, again, is easier. So eight basically means five plus three. You already know how to represent five, and you also know how to represent three. So you just need to put them together. So basically in this Roman numeral rules, right, most of the time is just a kind of submission. You have a few letters. In order to find the total value, you just need to summarize the individual values from left to right one by one. The only exception you see here so far is for the situation like a four, where you have to put one before five. In this case, it's a subtraction instead of submission. So far, that's the only exception. Otherwise, the rule is pretty simple. But then, again, when you look at nine, this subtraction rule comes in again. So in order to represent nine, you can't see it's five plus four. You have to say nine is equal to ten minus one. Then in order to represent ten minus one, what do you do? You just put i in front of ten. Sorry, you just put i in front of x, which is put one in front of ten. Then that becomes ten minus one, which is nine. And again, you have... Well, this is just rules that you find. So try to remember, try to follow. Once you get used to this kind of rule, it will be easier. So these are the basic rules. Actually, let me give you another more example. So how do you represent this 982 into Roman numeral? The logic is very simple. Firstly, you need to break this number into the digits. You need to break this into 982. Rather, if you think about digit, there's a one-digit non on the handle's place and another digit eight at the tens place. And the last digit is two at the ones place. So it depends on how you think about it. So you either think about this one as 900 plus 80 plus two, or you think about this one as nine at the hundreds place, eight at the tens place, and two at the singles place or ones place. So otherwise, once you break this down into three pieces, the task is just to convert each individual digit or number into the corresponding Roman numeral. And then for each of them, you just need to follow the rules that we shared just now. A 900 happens to translate to CM because 900 is equal to 1,000 minus 100. 1,000 is M. 100 is C. So you need to put C in front of M to follow this subtraction rule. That's why 900 is equal to CM. And 80, 80 is basically equal to 50 plus 30. Just now, I told you that 80 is equal to 5 plus 3. For 80, it's the same. 80 basically means that it's 50 plus 30. 50 is L, and 30 is basically 10 plus 10 plus 10, which is three triple X. That's why you come out with this rule, which is this one. Then two is very simple, just I. So now, once you have the representation for every single digit in this integer, you just need to concatenate them into one. And this is the final representation of this Roman numeral. So if someone asks you to give you an Arabic number in order to convert this integer into the Roman numeral, you just need to follow these two steps. First step, break it down into digits. Second step, for each digit, convert it into some kind of Roman numeral representation. Using either the summation rule or subtraction rule, I told you just now. And then you get the individual representation, and then you concatenate them. So that's the final representation. There are further rules, which are basically, I showed you some rules just now, like for X or I, basically for those values, represent 10, 100, 1,000. Those values, they cannot be repeated more than three times. That's one rule. Another rule saying that the I, they can only appear in front of V or X. So the letter I, they cannot appear in front of, let's see, 100. So that's another rule. If you like, you can take a screenshot of this slide. But then this rule is actually pretty complicated to understand and remember. I simplified it a bit. Okay, where are my simplified rules? Oops, I forgot to save it. Okay, let's go through these rules first. Maybe my rules are in the other places. Yeah, so the third rule, one says basically here says that whenever you put a smaller number in front of a bigger number, the rule is basically subtraction instead of summation. Also, there is no number zero in Roman numerals and there's no negative numbers. Actually, if you follow the rules we defined just now, actually there's also an upper bound, the maximum number that can be represented using Roman numerals, using those few letters, which is 3,999. So that is the maximum number if you follow the rules we described just now and using those few available characters. Yeah, if you like, take a screenshot. But otherwise, later you will do your Cata, but in the first round of Cata, actually these few rules are not that important. Otherwise, if you really need them, you can just search online, search on Google, just search Roman numerals. There are quite a few websites which basically tells you those rules. Later, if you need the rules, I can either share the slide on the screen or give you the link. Okay, so that's about the rules. And then, let's see, we're about 30 minutes into this talk. So let's start writing some code. But you would not write the code by yourself today. You're going to pair up with someone. Like two of you will be shared the same computer and the same keyboard. Basically, find your friend and then use one of your computers. And you will write your code in a ping-pong style, meaning that if I'm pairing with you, sorry, what's your name? Ping. Oh, sorry. Ping. Okay. Okay, I'm Pong. Okay. Okay, I'm Pong. So ping will write the test case, which fails. And then I'm going to write the code to make the test case pass. And then I'm going to do some refactoring to make the code cleaner. And then I'm going to write a new test case. So after I write this new test case, then ping is Princeton. She will make the test case pass, make the code cleaner, and then she will write the third test case. And then it's my turn to make the test case pass. So you select the table tennis or ping-pong where each of you just get a chance to do something. Typically you write a test case, make it a fail, and then it's your pair to her responsibility to make it pass. So you follow this style. And then you follow the TTT style as well. Basically that means you always write a failed test case to start with. And then we will do a 45 minutes per session, meaning that this afternoon we have about two hours, but we will not write the code all the way along for those two hours. We will break it into 45 minutes sessions, so we can possibly have maybe less two or three sessions. So after 45 minutes session, I will call a break. And let everyone stop. Then we can start to probably share what you were doing in the last 45 minutes, what's your learning, do you discover something new? You can share with your classmates. And then we will start again. But in the next session, you're going to delete all the code in the previous session. Start from scratch again. That's the basics of Cata. You can't reuse whatever you have in the last session. You start from scratch again. But then you will notice that you're getting faster and faster because whatever you did in the last session you still have the memory and you just need to redo those things. But then you notice that you will do it much faster. So this is why we call it Cata. You just do it again and again. Okay. So we will start the round number one now and you find your pair and then decide which computer to use, decide which language to use. Oh, you probably want to find out, so start with you probably want to find out someone who knows the same language as you. But if you would like to learn something new, you can also pair with someone that you use some language that you would like to learn. But otherwise, find your pair. Sorry, before you start, another just another reminder. Do not discuss a lot about how do you, your ideas, your solutions. Just quickly set up the project, write the first test case, and then start doing it. Do not put too much time on the design or thinking how to solve it. Let the test cases drive you. Shall we pause first? Okay, cool. So just now when you guys shared your solutions, I emphasize that you need to tell other people your test sequence and why do you choose that sequence. It's because in TDD, when you try to solve a problem, there could be different solutions to that problem. And when you write test cases, you are basically guiding yourself to generate a solution using the test case. So for different sequence of test cases, sometimes it guides you to generate different solutions or different answers. So for myself, when I did this exercise, the first time, the sequence I chose is I start from one, two, three, four, five, six, similar to one of the group that you were using. And then I generated one solution. Then the next time, I was trying, the second time I was trying, but then I adopted a different sequence or different solution. So the second time I was trying to, from one, two, three, four, until 10, that's okay, same as the first time I tried. But then instead of testing the scenario for 11 or 12, I jumped to testing the scenario for 20, 30. The reason is because the second time, probably because during the first time I got some insights into the problem, and then I did a bit of analysis. And I found that actually, there are some patterns in the Roman numeral rules. And for example, the rule for one, ten, one hundred, basically for the same rule, you can do the exact Roman numeral character for that rule, for that digit. And then the rule for two, twenty, two hundred, two hundred, in this case, two thousand, they're all very similar. You just repeat the one once. If you repeat the one twice, you get two. If you repeat ten twice, you get twenty. If you repeat one hundred twice, you get two hundred. So those few digits or numbers, they follow one rule. And similarly for four, forty, and four hundred, to me, they follow the same rule. Basically it's just a five minus one or fifteen minus ten or five hundred minus one hundred. Those few digits, to me, they follow the same rule. So when I look at those patterns, I feel there's this kind of a rule. And then how do I kind of write test cases to make those rules or patterns more obvious to me? That's why I wrote the sequence in this way. Purposefully I wrote test case for ten, then followed by twenty, followed by thirty. And then when I look at my code, I feel that, oh, there's a pattern. The logic is very similar to the way that I generate one, two, three. Between one, two, three, ten, twenty, thirty, one hundred, two hundred, three hundred. So I see a pattern there. So basically I did some analysis in the problem. I saw there's a pattern. I purposely changed the sequence of the test case to make those patterns more obvious to me. And naturally it leads my solution to a different structure. So basically for those two tries, I have different solutions for the same problem. And the second time is different just because I noticed the pattern and I changed the sequence of the test case to help me to make those patterns more obvious. So that's why I was talking with some of you. So when you write test cases for TDD, you probably need some kind of a bit of analysis up front and ask yourself, what is my first test case? What is my second test case? So how do I structure my test case to review the patterns that is in this problem? So that's a technique you can try when you solve your problem again. Okay, so let's take 10 minutes break and let's come back at 4, 10. And after that I would like you to switch your pairs. Do not work with your same pair again, switch your pairs, and start from the problem from scratch again. And then try a different approach if you like. Okay, so let's take a break.