Test Driving Code with the Help of AI - Venkat Subramaniam
About this talk
This talk explores the application of AI in writing automated tests for test-driven development (TDD), emphasizing the need for discipline and time commitment in the testing process. The speaker highlights that creating automated tests can directly influence code design and enhance feedback loops, vital for ensuring code quality. Through an example involving Conway's Game of Life, the speaker illustrates how AI can assist in generating test cases before code implementation while also pointing out the limitations of AI in producing optimal test solutions. The talk further discusses the balance between human expertise and AI assistance, concluding that while AI can aid in efficiency, it requires knowledgeable programmers to direct its capabilities effectively.
Full transcript
I want to talk about uh using AI to maybe uh help us to write automated test, test first development. But before we dive into it, one of the challenges is um we we can benefit from having automated loops. And automated feedback loops. And and the benefit is we are able to know when the code works and when it doesn't. It gives us the uh a confidence when
we modify code. It becomes a very important tool for refactoring. And yet, I think as an industry we have really struggled writing automated test. Uh part of the reason for that is uh writing automated test requires uh two things. It requires discipline, which often is in short supply. It also requires time commitment. And we always tend to be really under pressure to move faster, uh even though
we realize we cannot move faster if we don't have, you know, feedback loops. So my thought was, maybe just maybe AI will come to the rescue. Maybe AI can help us to write those test cases so that we can actually have these feedback loops built in. And that's what I want to focus on today is we want to see how we can use AI for writing test
before we write code. So uh test-driven development uh to me versus writing test after writing code. Well, one of the challenges with writing test after writing code is the test has the code has to be in a structure for us to be able to write the test. Uh unfortunately, you cannot just walk up to any code and say, "I want test on it." So, unless the code
has been designed for with the intention of writing test cases, it's really hard to write test cases on it. But of course, the question is if it's so good, why are we not doing it? Like I mentioned, discipline, time commitment. Maybe AI is the rescue. What can we do? So, what if we can use AI to drive and write these test cases, and maybe we can become
these smart navigator. But I want to talk about the reality of doing it, and this is where one of the one of the benefits you gain out of writing test before you write code is that not only can you have the test provide the feedback loops, but you can also influence the design of the code by way of writing these test cases. But on that point, I
want to emphasize one thing. Ignoring AI for a minute, if I'm writing test and writing code, one of the things I I've realized is, writing test and writing code is not a mechanical process for me. I don't find a value in writing test, writing code, writing test, writing code, writing test, writing code. Almost it never happens. To me, it's about writing test and then staring at it.
And then trying to think through the consequences of leading the design in a certain way, and reworking the test a few times before I even get to writing the code. So, to me, it's far from being this mechanical process of test and code and test and code. That never seems to be the case for me. It's usually write the test and stare at it, think about it,
revisit it, rework the test a few times, and then potentially writing minimum code, and then moving forward and repeating the cycle of the deliberation in the next test as well. Excuse me. So, as part of that, the one of the things is if tests can help us to drive the design of the code, uh how does that actually work? And to me, the design often is in
my head. And and I don't even know what a good design is yet. And I'm exploring through it and trying to really arrive at a better design by way of, you know, exploring with. So, I often say that my very first few test cases help me to tease out the interface, and the tests that follow help me to drive the implementation further. So, then the question is,
if the design is going to be in your head, how can AI somehow figure out what's in your head? How practical is that, right? So, let's let's think about this with an example. So, the example I want to use here, and we won't be able to complete this right now, but we'll we'll do as much as we can, is to really work with this this game uh
called the Conway's Game of Life. So, let's talk about the Conway's Game of Life and what it provides. It's a zero-player game, and in this game, you have a bunch of cells, as you can see in here. So, the cells could be alive or the cells could be dead. It's very easy to remember it. So, given a cell, if the cell has two uh fewer than two
neighbors, think of it as underpopulation, they die, uh or they don't uh uh get to become alive. And if the a cell has more than three overpopulation, it dies as well. If a cell is dead, it is born, it becomes alive, if it has two neighbors, sorry, if it has three neighbors. So, think of it as having a mom, dad, and a midwife for it to be
born. Uh if a cell is already alive, it stays alive if it has two neighbors or three neighbors. So, the rules are fairly simple that per cell. But, the universe could be infinite. There's no limits on the universe, the size of the universe. So, this is where we need to think about a design as well. But, let's say we want to build this, and we want to
build this using uh writing automated test. How would we proceed to do it? And as it turns out, there are some examples we can use from here as well eventually when you want to implement it. So, I want to start with this game. Where do we go? So, we want to create a test class. So, I have a test class right here as you can see. And
and this test class, uh as you would see, is a test class focused on a Game of Life test. That's what I called it. So, it's a class. There's not a whole lot in here. So, we start by using this particular uh class, which is going to have the test. So, I'm just starting with this fresh. The first is I often like to start with what's called
a canary test. A canary test is useful to set up the environment, so that way you know all the dependencies are in before you write any sensible code. Uh there are times when you write a few lines of test, and then things don't work, and you're trying to battle that. There's no reason to. So, I'm going to start with my very first test. We'll call it canary.
And the canary test simply says assert true to true. It's one of the most trivial test you could ever write. So, this test I'm going to bring in. I'm just going to use JUnit right now for for us. And then I'm going to say this is my uh test I have, which is called a canary test. And it simply asserts that true is true, so I can
simply execute this test and make sure the test is passing. But, it's a good idea in general to also verify if a you know passing for the right reasons. That's generally a good practice. So that way we can know that the the tools are working properly as well. So as you can just see that even didn't work. So I need to really set my version of the
language properly. I can go set this to false and execute the code. Make sure the test is failing. So that's a proof that this is actually working as it's supposed to. So just a warm generally to make sure that the environment is set up properly, the dependencies are in place. But now we have to write the very first test. So the question is what's the very first
test we are going to write? That's the first uh thought that comes to my mind, right? So let's go ahead and start with the very first test. So I'm going to say here is my first test I want to write. And and I'm thinking what could the test be? So we could ask for uh a very first set of uh test to be brought in. So I
am here in IntelliJ IDEA. I'm using Copilot uh in here to assist with this. And you can use any IDE. You can use any uh AI tool. Uh the rules don't change a whole lot uh basically based on what we are going to do here. So I want to start with the the very first test beyond the uh canary test for us to work with. So what
are we going to test in here is the question, right? So we have got to write some test in this case. So this could be a a game of life, right? That we are going to write the test for. And in this particular case the the Copilot is still not active here yet. So I'm not sure why it is it's not activated. So I did uh enable
completions. Maybe we can give it a little bit of a kick and see if that goes through. So let's let's give it a try. So let me just restart it and see how it goes. So, essentially, in this case, uh let's go ahead and restart it. So, um let's see if it kicks in to write the test we want to write and and bring that in in
here uh for us to use. Let me make sure I got the right uh code here that I want to bring in. So, so let's see if I got the right project. I will start over. So, if to bring in the right one in here. So, um all right. Where did the code go? Um all right. So, I'm going to blow this away. Let's go ahead and
start it and see how we can work with this. So, let's start with a little example here. So, I'm going to make this a little bigger so you don't have to stare at that. So, let's start with a game of life, right? That's what we want to start with. So, uh I'm going to say game of life, let's put a source, but let's put a test as
well. Within the source, I'm going to simply say game, we'll fill it in a minute. And here's a test, I'm going to fill it fill that in a minute, as well. So, let's start with a game of life. Let's see if the IDE kicks in now and brings things in. We'll find out in just a minute. So, here is the source. I'm going to mark it as
a source uh root uh just starting over. And here's a test, I'm going to mark it as a test root, as Uh and let's start with a little test to play with, right? So, let's go ahead and create a first class. We'll call it game of life. We'll call it test. So, that's our very first thing we're going to start with. So, as we did a minute
ago, here's my canary test, and we'll simply say this is going to be assert true, uh uh and that's going to be the test we're going to be writing. So, let's start with that very first step, and I bring in the dependency here. So, uh back here. So, bringing the dependency, let's go ahead and bring in this import as well and then run the test and we
are back to where we were a minute ago that's starting. Now, I want to first write my very first test, right? So, what are we going to do? Here is my test I want to start with. So, what should the test really do is the question, right? So, this one my There we go. Copilot kicked in and it wrote that test. It didn't put the curly braces,
but that's okay. We'll excuse it. That's what Copilot did. It wrote that, So, let's look at this. Give me a thumbs up if you like it. Thumbs down if you don't. In the middle if you're not sure. What do you think? I see a few thumbs up. What about others? Thumbs down a few. Thumbs up a few. Okay. So, one of the problems is and thank you
for that. One of the problems is we need to be able to evaluate the quality of what it produces. If we don't it's going to be TDD which stands for tab driven development. You're going to just keep tapping it, right? That's Something tells you that that that's not a good TDD, right? Where you keep hitting the tab and it fills the code. Do you think it's going
to work? That's not a good TDD, right? When they say TDD, it's not tab driven development. So, you cannot just be tapping it. But you look at this code, there's one particular flaw in it. If you notice, what does it do? It sets cell and then it calls get cell with a next generation in here. There are two problems with it, right? What's the first problem? The
first problem is that is a big jump, right? That's a very first problem. It just boom, I'm going to go to the next generation first. To me, normally TDD is where I incrementally grow the design, but this is just jumping into it. That's the first problem. The second you know, you know, you can say design that this test is leading to involves setting and getting state. I
prefer fewer state as much as possible. this is one of the things I'm not a fan of. When your design is only as good as what you create. So, if your design says product state your design is going to have a state. If your design is reduced state, you're going to have fewer state. So, it's what you choose. There's no right and wrong. There's only better and
worse. And so, as much as possible, I want to reduce the state. So, for that reason for me this receives a thumbs down. Because this is too much of a state-driven approach than to my liking. So, what am I going to do? I'm going to step back and say, "No, thank you. I don't want you to do that." And I'm going to simply say, "Test we'll say
dead cell behavior." Right? We'll start with a dead cell behavior. I want to take a very small step. So, the minute I write boom, it wants to fill up code. That's what it does. Have you had this situation? You're trying to write code and Co-pilot writes it for you. There is a name for it, you know that, right? That's called coding with the in-laws. Right? You know
how it is. You're trying to have a conversation and your in-laws keep interrupting you, right? How do you feel? So, that's coding with the in-laws. I'm like, "Copilot, could you please calm down? Could I please do my job?" So, I usually turn it off. Like, "Peacefully, give me 5 minutes to write some code, right?" So, essentially, this is eager to fill it. So, but again, notice not
very different from what we just did. So, I'm going to say, "Thank you, but no thanks." Uh uh assert equals in this case, assert equals. And notice what it says, false. Why does it say false? The reason is, the minute I say assert equals, it's like, "Aha, you're trying to implement this next state function." And what did a lot of people do? They decided to use true
for alive and false for dead. So, it's like, "I'm going to use true and false." So, what AI does is not what is right. What AI does is what most people have done. Now, just because most people have done something doesn't mean it's right. It just means that that's what most So, it just follows what it has learned from. Here's the problem you need to keep in
mind. AI has been trained on a lot of code. Raise your hand if you think overall, considering the world full of programmers, raise your hand if you think humans have written really good Can you raise your hand? No, a lot of thumbs up. Just don't raise your hand otherwise, right? Not a single person raises their hand. Raise your hand if you think humans generally have written terrible
code. Yeah, right? Now, guess what we did. We told AI, "Here's all the code for you to learn from." We forgot one small detail. We forgot to tell AI, you know this code? That's a good one. You see this code? That's bad. We never told AI what is good and what is bad is what is bad. So what did AI do? It drank the entire ocean of
the code and it's like, I'm ready. And so when you say go, it's warm it's what it has taken in. And so no wonder it's going to produce terrible code to begin with. But one of the tenets of good code is a code needs to be expressive. And test cases give you that ability to write better quality code. So I'm going to say no thanks. Dead, {comma}
next to state zero, right? So essentially the next state when a cell is dead and has zero neighbors, the next state is So assert equals, let's bring in the dependency, but what is dead after all? So the dead is going to That's kind of scary. com.sun.tools.java, no. That's not what we meant. So essentially we want to bring in a definition of the state, isn't it? So I
go in here and I'm going to say in this case, this is new uh enum and I'm going to say this is a cell And the cell state in this case is going to be a value of dead, right? We brought brought that in for the dead. That's all we are bringing in. And so once we define the dead, we can go back here and we can
say, hey import uh game uh static, right? Uh game.cellstate.dead and bring that in. That's your dead state. Hey, what is this next state? Next state is in game of life, right? In this case, that's got an upper case, which is kind of nasty. We'll refactor that. So, here is the game of life test. Fix that. Great. So, now I can go back here and say this is
game of life uh uh dot next state. So, that's a static method. What is game of life? We will create game of life here as a class under the source directory. And let's go ahead and make that as an interface. I'm not going to keep any state. And within that, we are going to implement the next state as a static method. So, here is the you know,
cell state. We'll call it current state. And this is going to be number of live neighbors, right? Uh so, uh of course, what we can do is we can ask it to write minimum code, so this returns a dead. Where dead is going to be a static import again, so we can say this is going to be import, and this is going to be a static uh
this is game dot cell state dead. So, we can write that minimum code and get that to pass, right? So, that's our very first effort here. So, let's go ahead and run the test and see what it does. So, that's great. Now that we wrote that, it's our step to go forward with the next one. So, let's go ahead and say here's my next set set of
steps test. So, what is this going to be? Test. Then, you know, we could say more dead cell uh behavior, right? So, we can say more dead cell behavior. But, what is this going to do for the for these behaviors is the question. Boom. It wrote some more test. What's the good news? The good news, it learned from what we just wrote. And said, "Oh, for one,
for two, for three." Not that it is correct, but it did make an effort. But let's stop right here. Thumbs down. Why is it thumbs down? Because the value is incorrect or any other reason? What was the reason it's not good? Aha. There's one person who knows what he is doing. One of the tenets to follow is this. I'm not worried about incorrect value yet. But one
of the tenets to follow is never write multiple independent in one test. Why? If one assert fails, the rest that follow do not run. This is another tenet. But guess what? AI didn't know that. Because it did it learned from wrong examples. It looked at people writing 20 asserts that isn't that cool? It's like no AI, that's not cool. But nobody told AI that it's not cool,
right? He didn't supervise AI, right? If AI had read his code, it would know not to do that. But yet it did. So, what are we going to do? Let's go back here and say, "Hey, maybe I don't need this after all because it was useful to get that trained in this case, right? Let's remove this as well, and we will say, "This is my test, a
test that cell a behavior, right? And then in here we'll say, "Assert all." And within the assert all did you notice it knows how to do it? It just required a little bit of your effort and it fills in the rest of the So, it immediately said, "Hey, here is a dead, here is a dead, here is a two that's dead, here is three but that should
be alive and then finally that's a four and that should be dead." And what is alive in this particular case? Alive is going to be just another one in this one rather than dead we're going to put dead and alive are two things we're going to add to this, right? So, we and alive is going to be just another import that we're going to bring in here
and we're going to use it. So, that becomes our test right there and the test is going to fail right now but that's fine but it allows us to write the test with an assert and in this case assert all. So, what does an assert all do? An assert all runs each one of the asserts. So, if this was JUnit 4, I would be writing multiple test.
In JUnit 5, an assert all is as all is useful. But use assert all only when the asserts are independent of each other. So, this is a better way to write the code. But you also noticed one thing. The minute you put assert all, what did it do? It knew where you are going and so it filled in the code. So, I feel like AI is like
this little dog you take on a and the dog doesn't know quite exactly what your intention is. So, it wants to go straight, you give it a little tug and you turn, now the dog knows where to go in your direction, right? So, at some points you have to really give it a little tug. So, it has a starting problem. It doesn't know where to start. It's
got a problem to turn. It doesn't know where to turn. But the minute you show what direction you're going, it immediately learns from you. But guess what you can do. You can write skills that says, "Hey, don't put multiple asserts, independent asserts in one test." It'll learn and start using assert all. You can put a skill that says, "Prefer minimum state in your design as much as
possible. Design stateless where possible." You If you start dropping those into your skills, then AI will start leaning more towards that because it can start learning from your world. So, this is one of the fundamentals to keep in mind with AI. It's been trained on the vast amount of information without quality metrics. But your skills can take this world of knowledge and distill it down to what
you think is better quality. You can then say, "Of all this knowledge you have, these are the things I want you to care about. So, here are the do's and here are a lot of don'ts." And you can start dictating them in your in your skills, so it moves in the right direction. Now, obviously, this is going to fail right after all because we didn't implement the
code yet properly for this. So, let's go back here. And and what are we going to do? You can simply say, "I want to implement this code." And at this point, you can see it's writing the code. You If it's If If you're happy with it, you can keep it. If you're not happy with it, you can, you know, ask it to re-implement it. So, for example,
we can say, "Implement this using ternary operator." Right? So, you could ask it just by way of writing a comment and say, "Could you not do that for me, please?" Okay, no word please, but without the please, and it immediately realizes, "Oh, that's where you want to go." And it's able to write the code. So, yes, we can be frustrated. It's not doing what we would like,
but in all fairness, it doesn't know what you like. But if you kind of point in the right direction, it's more than happy to fill that in. And in this case, we just wrote that. You can remove that line of comment right now. You can come in here. You can run the code and make sure the test is passing, and you can move forward. So, it has
the beginning problem. It's got a starting problem. It doesn't quite know where you want to go, but having said that, what's the next test that we can write in here is the question, right? So, next to test, what are we going to do for the next to test? So, we can say next to test, right, for a live cell behavior. So, it wants to write the test
for the alive cells. Um not bad, right? So, it was able to implement that part the minute I said next test, it figured out it needs to write a test for a live cell behavior. So, you can see all of these are alive, and then you can see for two and three, it's the right value. It figured it out, and you don't have to write anymore. So,
so far so good, right? What is the next thing I want to do? If I say next to test, what does it write is the question, right? What is it going to do for the next to test? It says, "Test the behavior of cell with eight neighbors, the maximum number of neighbors a cell can have." So, it is really struggling to figure out what the next test
should be. But, this is where your design really comes in. So, for me, I want the universe to be infinite. If the universe is infinite, I unfortunately cannot take a design where I take all the neighbors for a cell and start processing it because how far do you go? It's infinity. So, if the universe is infinite, design a grid-based system. Because when you define a grid, the
immediate question is, what's the size of the grid? Well, the size of the grid is infinite. Well, duh, if it's infinite, how can I represent it, right, in the So, we got to rethink about it. Maybe you can ask AI, right? How would you design it? Let's Let's see if it helps. So, if the uni- So, since the, right? Uh since the universe uh is infinite, uh
infinite, uh how can we design uh that, uh you know, in this case? So, you're asking, you know, do you have any recommendations as to how that could be done? It says we can use a map to store the state of only cells that are alive, and the rest of these cells are implicitly dead. This way, we can handle infinite universe without running into memory issues. Hey,
not bad, huh? So, that seems like a nice little approach because what we can do here is we can store only the live cells, and there's an assumption here that life is sparse. Now, how does it know this? It's because there are others who have done it. And so, it realizes, "Hey, I've seen this somewhere. Since you asked me how to do it, I can steal it
from them and give it to you." Like, in in the university, we call it plagiarism. In this world we call it AI-driven programming, go figure, right? Until the first lawsuit of appears. So essentially the point here is it figured out somebody has done that in a similar way and it drags you that solution in here, right? So in this particular case it's a sparse, you know, matrix
kind of an approach, which is what I would I would do actually, which is what I've done it in the past. So I'm going to say a test. So this is going to be my avoid test uh generate signals for one cell. So I'm going to take a signals-based approach. We'll see what this is in a minute. So in this case I'm going to say assert assert
equals right there, right? Equals and this is going to be a generate signal. So it's going to generate a signal for a cell. So list of and we'll say this is going to be a bunch of cells for which we want to generate the signal. So we will say generate signal for one cell and we'll call it generate uh signals And what is the cell I want?
We can say a cell at one {comma} two. So we'll we'll get back to this in a But the question is what what is going to be the result of it? We need to figure out the result. We'll get to that in a minute. Uh in here, but we'll take baby steps. But what is cell after all? So we need a cell function. So let's go over
here to write a cell. So cell uh is going to be um a class we are interested in writing. So we'll simply say null for a minute. We'll come and build it in a minute. So what is the cell? I'm going want a cell and I'll create as a racket. And that's going to be in the source directory. And my cell racket contains uh X and a
Y, but it also contains a public static a cell function which takes an X and a Y and returns, this is more of a convenience function, which returns a cell for us, right? So, now that we wrote it, I can then say, here is a cell, but what is cell? It's a static import of that. So, generate signals for us for a cell. So, what is going
to be the signal for that cell? It's going to be a list of cells, right? So, notice it says cell 0 1 and a 1 0, that's not correct. So, cell is correct. So, it's a 1 2, right? So, we're going to go to 0 1 and a cell uh 0 So, 0 uh uh and a cell 0 uh 3, right? Uh and then a cell uh
this is going to be a 1 and a 1 and a cell 1 and a 3, as you can see, because you don't want the cell itself to generate signal on itself, right? So, you're going to eliminate that middle And then you're going to say a cell at 1 Sorry, 2 {comma} 1 and a cell at uh 2 {comma} 2 uh eventually. Uh and then finally a
cell at uh if you will, uh cell at 2 and 3. So, those are the values that you're going to bring in for that uh list of cells. So, let's go ahead and say, here's a list of cells and the generate signals for cell. What is the generate signal for cell function? This one comes from Game of Life dot generate signals, right? So, that's a static input
we'll bring in. So, here is a list of cell and generate signal for that particular cell. So, what does it return? Return and it's a list of uh Look at this for a minute. Notice it said cell with offset. Anyone can imagine why it says cell with offset? Do we have a function called No, we don't. Then, why is it writing Because it saw something somewhere. Remember,
I I know I know everybody in this room is an exceptional person, But, remember when you were in the you're taking the exam and this dude is trying to look over and find the next person. And they didn't realize this person was taking a chemistry exam, they were taking a physics exam. It doesn't matter, right? And they just wrote it. Hey, look what they're writing. This is
AI has been trained, as you can see. So, it said, "Look what I can drag. It's a with offset." And you're like, "Really? Where did that come from?" So, this is like the The equivalent of this is what did the cat drag into the house? Not a pleasant one, but it just said, "Boo, I got something for you." That is another thing we have to be careful
about, right? And you're like, "No, thank you, though. This is going to be a cell, but what is the cell going to be?" Oh, first of all, let's say X is equal to cell.x, right? We're going to And Y is cell.y, and here is a cell of X minus 1 and Y. And by the way, did you notice something here? It nicely decided to structure this. Now,
obviously, what is cell, you know, that's not a good way to write it. We'll say a cell for me, a cell. So, we can easily reference it. This is a cell, and this is a cell as well. So, we can use the cell here as a static import. But, I want you to pay attention to one thing here. If you notice, notice how we structured this nicely
visually. And you know why it did that? Because I I wrote it last time like that. And it's like, "Ooh, I like it, Venkat." And it decided to keep some of those good parts in it. So, it visually shows that the cells are, you know, structured this way with a little hole in the middle, so you can see which cells you are generating signals for. But again,
it's seen me write that and remembers it and says, "Ah, you like this, don't you?" And it's trying to please me right now by writing it that way. That's basically what happened So, we wrote that, and that's exactly what you saw it try to do here as well. When we are writing this, if you were to now take this away and notice, that's exactly what it did,
knowing that I like that structure. So, it it structures that using that particular, uh, you know, syntax, if you will. So, this is the list of all these cell values, and then the game, uh, you know, of life, and this is the generated signals, and and that's going to be an, uh, you know, static import that we're going to bring in. Uh, what's the problem here, though?
This is saying, uh, cannot resolve the method equals, and it's a list of all these cells. Well, that's going to be a list of cell 01 11 21. So, those are correct. And then ending here with that cell itself for that value, and this is ending the you know, uh finally that that one also. Let's make sure the syntax is correct. So, so that's an extra one
I think here. So, that's why it's having trouble. There we go. So, that's basically the code generation kind of fumbles from time to time. So, that's fine. So, now that we wrote that, what's the next test we're going to write here? So, this next test says, uh test generate another cell. So, this is for another cell we're going to write it. Uh notice it was smart enough
to put two and implement that code. Let's pause here for a minute and appreciate what you just saw here. So, I've been doing TDD for a while. So, what do I typically do? I wrote the first test as an example. And then you want to write a second test which is very similar. So, what do you normally do? What was that? So, you have one test, you
want the second one to be very similar to it. What do we normally do? Copy and paste it. How does it feel? Not so good, right? But, you don't want to be typing the whole thing again. So, you copy and paste it. You start changing the numbers. And if you're as good at it as me, you make some mistakes while you change the numbers. And you spend
the next 5 minutes trying to fix it, isn't it? This is the part I love about what AI It is like, "Dude, you wrote one test, you need a second one. Let me write it And thank you. I don't have to be writing that, right? I can just keep hitting the tab and move forward. That becomes a lot easier. Just verify the test is correct. And yes,
it's a different number, a different cell, and so that is correct. That's great. So, then what are we going to do? Here is our next test. And what does this test actually provide is the question, right? So, what does this next test provide? So, we can say next test. So, what could the next test be? What should it actually do? Is to test the behavior of a
cell in the context of his neighbors? No, it's hung up on that. So, this is going to be the next test I'm going to say over here. Uh uh avoid and test generate signals for test at origin. But you and I know one thing, there's no real concept of origin, right? So, It has a turning problem. It's got an ending problem. Other than that, everything is great.
So, essentially, you have to direct it where it needs to go. So, in this case, I say thank you, but I'm going to move on to a different direction. A test generate We'll say generate uh an empty uh set uh list of uh sorry, set of cells. So, for an empty set of cells, I want you to generate the signals, right? So, what are we going to
do? Assert equals. This is going to be a list of empty, nothing. Generate signals for cells, right? And what is the input? A set of empty. So, that's basically what we are going to write. So, I give a set of cells, and it's going to generate no signals for me. This is just the very first step I'm writing. So, here is that set. We're going to bring
the set in, and here is the generate signals for cells, which we have brought in here uh as an static import, right? So, import static a game. dot a game of life. Generate signals for cells, which we don't have, but we are going to implement it. So, let's go over here and we say this is static list cell generate signals for and this is going to be
in this case. So, generate signals for a set of cell, we'll say cells and what are we going to do? Return a set of and return an empty set. That's what we're going to start with, right? It's a list of, pardon me. And we're going to return that particular result back from So, then we'll go back here and make sure the test is passing for that little
test. That's our very first test. Then we can say, here is my next test, right? So, this is going to be test generate signals. We can say uh you know, signals uh a far uh a set of one uh cell. So, this is going to be a set of one So, what does it do? Notice it's one and two and those are the results for that. So,
obviously the test is going to fail right now because that code doesn't exist. It's time for us to implement the code. But let's just spend a minute for it before we go further. So, in this particular example it's going to return all the cells for that particular cell that's given, right? So, that's not a big deal. But let's go a little further to think about it. If
I say here void test for two cells That's pretty darn good, isn't it? It gave us for one, two, and two, one. Look at all that time saving not having to type it. Just verify it's correct. So, for one, two, it's a zero, one, one, one, two, one. Ooh, that's wrong, isn't it? So, it should be zero, one, uh zero, uh two, and a zero, three. That
should be the values for it, right? So, it just puts out something may not be right all the time. So, fix that. But, now that you've fixed it, maybe you can just go back here and let it redo this. And and at this point, it's it's writing uh the values for it. So, this is going to be uh what's the next step here? So, it's got to
fill in zero, one, zero, two, zero, three, and then sell the next one here is going to be one, zero. Is it correct? And one, one, and sorry, one, two, but it shouldn't have the one, two here. So, this is going to be one, one. Still not getting it right. And then this is going to be empty. You don't want that. And then it's going to be
a uh one, three. And the next one is going to be a two, zero, a two, two, right? So, two, one, uh two, zero. So, it it butchered this this time. And then it's going to be a two, uh three. Uh so, this is going to be a two, two, right? So, two, one, two, two, and two, three. But, that's not it. For the other cell, it
needs to do this also, right? So, this is for the two, one, and that's going to be a one, zero, uh a one, it's going to be a one, and then finally, that's going to be a one, two, as well. So, you can work it. So, essentially, once you finish it, uh what is going to happen at this point? This is going to be a two and
one. So, this is going to be a two and one. So, that's a two zero. And uh that's going to be the gap. And then a cell at uh two {comma} two. And then finally, the last set of values is going to be a three one. Uh uh sorry, three zero. That's correct. And a three one and a three two. Assuming all that is right. Uh you
can see if it's going to work. But, the question is this. How do we implement the code for this? Again, these are the places where the inference can really work to your advantage, right? So, that failed. But, and have it implement the code. And I would ask the question, how many of us realize it's a flat map problem? That's a pretty good deal, isn't it? So, it
is able to realize it's a flat map problem. So, it calls the first gets a stream on it, puts a flat map on it, and returns a two list on it. So, that's the implementation. Let's see if that actually works. So, let's run through that. One test failed. What's going on? So, let's make sure this is actually correct. This is generate for two cells, right? So, this
is a two One two. So, that's going to be zero and uh two. So, that's a one two and three. So, that is correct. But, in this case, it says zero one, whereas it's one zero. So, what's happening? As it turns out, let's see if you're able to figure out what's what's going on in this code. Anyone has a clue what's going on? Let me give you
a clue. What's that? You are iterating on a set. What doesn't a set guarantee? Order. That's the reason it failed, right? So, you may have to fix that order. So, you could say, here are my cells, and you could say sorted. You could sort on it. And what are you going to sort on? Comparing, right? So, comparing uh based on cells X, and then comparing uh so,
then comparing a cell Y. So, you could write it as a as a way to perform the comparison on it. So, this might help you to put an order to it. And you can come back and run the test and make sure the test is passing. Let's see what's going on. A 0 1 a 0 So, 0 1 and a 0 2 and a 0 3, whereas
this is a 1 and So, it should be actually in the order, right? So, this should be a 0. All the X should be there first, and then all the Y should come in after that uh in in terms of the response, right? So, let's see if we can move this up here and and have the sorting done on the result rather than on the original, right?
That's what you want to sort on. Uh but, you got to go back and make sure the values are actually correct. So, it's a 1 1 versus a 1 1 0, right? So, this is a 1 1. Is that a correct 1 1? That should be a X is 1 and Y is 1, right? So, it should be 1 1, whereas for some reason it's a Y
0. So, maybe I made a mistake in here uh or it made a mistake. Let's figure it out. So, this is X minus 1. This should be an X. So, there was a bug in here uh that that we left unnoticed, I think. Let's make sure that's actually true. So, X and Y minus 1, this should be an X itself, right? Uh on the value. So, X
- 1 Y - 1, this should be a Y. Uh this should be an X - 1. So, overall, that was an incorrect value as well because we're keeping the X as - 1 to begin with, and this should be an X - 1 as well. So, you can kind of see where that problem, you know, came. That reminds me we have to go back and verify
that this is actually correct. And some reason, what it generated here is not correct. But, you get the point, right? So, you got to fix that. So, once you do, that gives you the idea of how you can generate it. So, what is the moral of the story here? I'm going to actually regen this if you don't mind. So, let's go X - 1 and X -
1. This is going to be a Y - 1. This is going to be a Y. And this is going to be a Y + 1. And likewise, this is going to be an X and then Y - 1. And this is going to be a cell. Again, this is going to be X. But, this is going to be Y + 1. If I'm doing it right.
And then finally, this is going to be an X + 1. Whereas, this is going to be an Y - 1. That's the order in which I want to process that, right? And then this is uh + 1 again. But, this is going to be a Y itself. And and Y + 1. Hopefully, I got that right. We'll figure out. So, then this is going to simply
do the sorting and return. So, that makes the question, what should the result here be? This should be a 1 2. So, that's a 0. This should be a 1. And this should be a a 2, right? And so, 0 1 2. Similarly, this is going to be a 0 1 and 2. And that's going to be a 0 a 1 and 2 as well. And this
one is going to be a 1 all the way through. That's a 2 and that's a 3. Again, I could be making mistakes as I'm seeing these numbers, but let's let's find out. There's probably a couple of other tests that need to be fixed, but we'll get to that. So, this is going to be generate for Again, this one needs to be fixed for the other cell.
So, this one is going to be for the cells two and one. So, that's one, two, and three. And this is going to be a zero and a two and a one. So, seems like it's heading in the right direction. Right? Uh not yet. So, two and one. So, that's going to be one all the way through. That's a two and that's a three. And this is
going to be a zero. This is going to be a one. This is going to be a two. So, looks like I messed up somewhere. Where is that? Anyone sees the error? So, this is one, two, and three. One, three, and one, two, and three. This is all zero. This is one and this is two. Unless I made a mistake in here again. minus one and X
itself and X plus one and Y minus one, Y minus one. There we go. Uh and Y and Y plus one and Y minus one, Y plus one and Y minus one, Y and Y plus one. Yeah, I can't uh somewhere it's messed up. This says on another cell it's not too happy. So, this is going to be a 101 0 and 11. Right. Wait, which line
number am I miss- missing? >> I think it's on line 82. >> 89 should be a >> Why should be a zero? Aha, this one here you're saying. >> What? Right, so >> Is that what you're saying? This should All right, so good. And this should be a zero as well. Got it. Thank you. See, you restored faith in pair programming one and that should be a
two, huh? Okay. So, this should be a two and that should be a one. All right. Let's hope that that's that's where it's heading. And uh then this one here where it has two cells in the set and this is set up one cell and this is a signal for one cell. So, this one is messed up, too. So, you got to fix it. So, 0 1
2 0 2 and And this one is going to be a one, isn't it? So, this should be a one, this should be a one, that should be a one. Am I right? And likewise, this should be a two and finally, this should be a a a three Yeah? So, anyway, you can proceed with that. This is moving on to the next function, one cell and two
cell, but a similar problem with this as well. So, you know, again, the generation should be relatively correct but sometimes it messes up, as you can see, and and the tool is rather imperfect. This time it got it, it was able to write it. Let's see if it's able to fix it for this one uh based on that as well. And uh yeah, it didn't quite complete
it, as you can see in here, right? So, it it couldn't finish it. It's just struggling with that. But anyway, you can you can work through that and have it uh complete the implementation. So, this is going to be a cell. This is for one and two. So, you start with a zero and one and a zero two and a zero three. That part is correct. And
this is a one zero and a uh two zero. So, I think that part is correct so far. And for this one, similarly, it needs to implement the proper uh you know, values, right? So, anyway, so you can work through that for the second uh cell So, what's the moral of the story? You can then test it for the next generation But, the bottom line is this.
You can use AI to do test first development, but when you do, it's going to do the driving, but you have to be the smart navigator. So, what that means is you still have certain design in your mind, the design you would like to pursue, things that are more stateless versus stateful, uh what kind of functions you want to define. So, you may have to really guide
AI to create a certain type of test. But, as you're guiding AI to create those kinds of test, it becomes really fast typist. So, it may not be the smartest tool to write better quality code, but I would argue the smartness is in you as a developer. So, here's the question to ask. Can I less knowledge and will AI do the job for me? Or, do I
have sufficient knowledge and will AI do the typing for me? AI is not in a state where it can do the job for me. It does things, but it doesn't mean it's quality things. It doesn't mean it's doing the right things. the cost saving for me is one of the problems I have is the following. I can type really fast, but I I never type at the
speed of my thinking. That's the problem. I haven't figured that out yet. If I can type as fast as I can think, then that's great. But since I cannot type as fast as I AI can do the typing a lot more effectively than I can, no matter how good I'm in typing. And AI is also fairly good in learning from what I am doing. And that's one
of the things I find is really effective is that AI is able to really uh move forward in the direction I want it to move with a lot less effort because it's constantly observing and constantly learning from what I'm doing as well. So, those are some of the observations. So, the point is this, AI it's a driver that matches the navigator's intelligence. So, so the problem here
is how good is AI? And the answer to me is It's as good as we are. So, if we're really good, AI is going to give us better results. If we are not really that good because we don't have the skills, then AI is not going to suddenly give better results than we can. So, AI is going to be a reflection of our capabilities. So, what that
means is the more educated we are, the more knowledgeable we are, the more skillful we are, we're going to get better results from it. We were talking about this during the break. One of the analogies I want to make is the following. If you look at other forms of industrialization, when we had crane, before we had cranes, what did we do? We We gathered up 10, 15,
20 people, and they had to move heavy things. Well, what did, you know, crane do? Crane allows us to lift even heavier But, to operate a crane, you need more skill, not less. So, my argument is, AI requires more skill from you, not Because, if you have less skill, you're not going to be able to operate this. You don't want somebody like me to operate your crane,
that's dangerous. Because, I don't have training in operating a crane. You want a crane craftsman to operate a crane, right? So, and less effort. And that is really the moral of the story. This AI demands more skill from you and less effort from you. And that's where I think we can get the productivity. So, AI is a driver that matches the navigator's intelligence and skill. And I
think the more skillful we more we can get out of it. So, that's basically my observation in using these tools. Hope that is useful for you, too.
More from this event
See all 126 talks →
AI Is Not the Risk. Architectural Drift Is - Sunil Kalkunte
17:39
Breaking the Monolith: Tesco’s Journey to Federated GraphQL with xAPI - Vishwas Chandrashekar
29:13
A Practical Introduction to LangChain4j - Venkat Subramaniam
1:01:28
Beyond the AI Models: How Lowe’s is Building the Store That Knows - Swaroop Shivaram
13:59