 Good morning. Good morning. Good morning. Hello. Okay, so earlier this year, conference somewhere in Germany discussing with people over dinner what topics people might be interested in that I talk about at some future conference and what I would like to talk about at some future conference. And I had an idea for a talk, but I couldn't come up with a title for that. So I asked the internet. Pure internet. How does PHP Uniforce write words like that? Why did it not work like that? And what I'm doing about it describes what I want to talk about. Can you give me a better title? Because that's rather long. So I got a reply from the PHP Conf Asia Twitter account basically saying we don't care about the title, just give the talk. A couple of minutes later, I get an email from Michael saying, hey, I meant it with a tweet. That's serious. Can you come? Can you get the presentation? So somehow we made that work. So thanks for inviting me to be here. So how PHP Uniforce works, why it works like that, why I wish it did not work like that. To get the idea, it's rather long. So let's make it a little bit smaller, like making PHP Uniforce better. Okay. So hi, I'm Sebastian. I help teams build testable software with PHP and implement efficient test strategies for that. That's the company that I do that with. A company that I founded together with Anna and Stefan some 10 years ago now. No more sales talk. So I'm not as old as Rasmus. I'm a little older than Derek. Who will get that 40 thing, which my knees thinks is a really large number and 40 is really, really old. He'll turn 40 later this year, though. Yeah, the same vintage. So 40 years ago, I was born. 20, eight years ago, I got my first computer. What's that one? So it's recognizable that there's a computer and maybe a CRT screen, but the black thing on the left, I'm pretty sure that there are people in the room who have never heard about tapes. They used to be a thing. It's now almost 20, a little bit over 20 years ago that I came in contact with PHP for the first time. 18 years ago, since I presented at a conference for the first time on PHP. PHP was the fifth programming language that I used and learned and used. So the usual suspect in the beginning, basic, that some Amiga basic when I got the Amiga 500, took me a couple of weeks to figure out that basic is probably not going to be the best solution to do nice things with graphics. I mean, the Amiga had really great graphics capabilities, but I couldn't get there through basic. So I started to look into C, quickly realize, yeah, that's nice, a lot nicer than basic, but still not fast enough. So I delved into assembly and then later on towards the final years where I was using the Amiga as my only computer, I went up the stack again, went farther away from the CPU, and ARAX was the first scripting language that I used. And then when I got my first X86-based machine in 1998, I quickly afterwards started to use PHP. At some point, 17 years ago, there was a bet between me and one of my computer science professors that it would not be possible or feasible to implement something like JUnit for PHP. He agreed that it was a good idea to be involved in open source, that I was active in the PHP community and things like that, but he said, well, if you want to do real development, then you have to do Java because we have JUnit and other nice things and then a week later, I showed him the prototype of what became PHP. So that's how that started. When I started to do PHP. I was about the only person in the PHP world that was interested in testing. I was very much alone with that. That didn't feel very good, but like within two years or so of the first version of PHP. I somehow managed to get a talk accepted at a PHP conference about PHP. I had exactly one person in the room listening to my talk, but at least I wasn't alone anymore. Thankfully, that has changed. So I'm talking about PHP. I'm here today because it is my project and I know a lot about the project, both on a technical side of things, but also on the history side of things. But what I talk about today likely applies to most open source projects out there and it may even apply to non-open source projects. So, looking back in time, I do not have version control records before 2006. They're gone. 2006, there were 13,000 something commits to PHP on it. I made 82% of that. I'm not saying that because I would like to brag. I say that because I'm ashamed of that. It should be a lot less that I actually do on it. And most of the talk will be about why that is and what I'm doing to bring new contributors to the project. So these are the top people that have contributed to PHP unit since 2006. As I mentioned, unfortunately, I'm in the top one spot. Looking back in time, 10 years ago, 2008, only two people contributed to PHP unit. One of them was me. I made 96% of all the commits in 2008. The other developer was Michael Lively, who worked on DB unit and some other non-DB unit related things in PHP unit at the time. So this year, so far there have been a little bit over a thousand commits to PHP unit in 2018. I made 71% of that. So that is a lot less than it used to be, but it's still not good, still not great, but it shows that what I've been trying to do over the last couple of years is starting to show an effect more and more people are coming to the project and contributing. And to give you a little bit of an encouragement, if you look at... So this is what 2018 looks like. Get back to the encouragement in a little bit. So why did it...what happened in the last 10 years? One of the main things that happened in the last 10 years is that I switched version control systems because technology evolves. When I started the PHP unit project, I used CVS, which was the second version control system I ever used. Back on the Amiga, I used RCS, which was a single user version control system. So CVS was a step up from RCS in that it had network capabilities and multi-user support and so on. But it was not really nice to work with and I'd rather not talk about CVS because that brings back really, really bad memories. But I chose it for PHP unit because I knew it from the PHP project and it worked. So from 2001 to 2006, I used CVS for the PHP unit project. CVS.php.net, in fact. Then in 2006, I migrated to Subversion, which was a huge step up. Suddenly, branching was painless, merging not so much, but branching. So in CVS, branching and merging was broken or unusable, in Subversion, at least 50% of that was not broken, so that was kind of better. But the big thing happened in 2009 when I migrated to Git. That made branching, merging easier but it also allowed, thanks to GitHub, people to contribute to PHP unit much easier. Before, they had to send me a patch via email and when I applied the patch, authorship information went away because I would commit the patch. Yes, in the commit message, it would say I got this patch by email from this and this person, but that's not really nice. You want to have your name in there and it's discouraging to people if they do not get authorship. Okay, so GitHub changed that. That's nice. So looking at all time commits since 2006, this is the encouraging bit that I mentioned earlier. Two of the top ten contributors only started to contribute to PHP unit recently. So if you want to contribute to PHP unit, it doesn't take that long to become one of the most active developers. So for instance, Andreas only started contributing last year at the first PHP unit code sprint. Within two days, he learned how to contribute to PHP unit and made really valuable contributions and hasn't stopped doing that since then. And I'm not... I tried to pronounce the name correctly. I don't know, evote. Maybe Derek can correct me on that. Really nice guy who suddenly showed up this year in April and is now the top four contributor of all time. Doing lots of great stuff that went into the PHP unit 7.2 and PHP unit 7.3 releases. So GitHub helps. GitHub lowers the barrier of entry at least on the version control side of things to contribute. You can easily fork the project, work on your own, figure out what you need to know to implement the feature that you want or to fix the bug that you're interested in fixing and then when you're done, you can send a pull request and that's really easy and no authorship information gets lost along the way. But if GitHub helps, why am I still the one with the most commits? So that's where this comes into play. Let's make this a test of how much older I am for real. Who remembers this game? So obviously this is a screenshot from a game. Rasmus remembers it. Okay. That's from a game called Dungeon Master. If you start Dungeon Master, the first thing you see after the logo is this door to the dungeon. Now you may wonder why is this guy talking about a game from the 1980s? Well, it turns out that the term of Dungeon Master also plays a role in software engineering. Alberto Brandolini wrote an essay two years ago where he talked about the Dungeon Master and the dark secret of the Dungeon Master is that he knows every trap in the existing legacy software because he was the one to leave the traps around. I didn't put these traps that raised the barrier of entry to contributing to PHP Unit intentionally. I placed them, I made it hard to contribute unintentionally and I'm now trying to clear up those traps. So sorry, I became the Dungeon Master of PHP Unit. I do not want to be that. I like being the Dungeon Master at a table with pen and paper and playing games, but not in software. So, to some degree, that is tied or ties back to how I got started in programming. When I started programming way back when, these were the kinds of books that I read that taught me to program. And these books were really great at explaining at which point in memory do I need to put which bit to paint the pixel on the screen. These books don't really tell me anything about or didn't teach me anything about writing software that I can understand a week later, a month later, or that somebody else that wants to work on the software can understand and build upon. So, a book like that was really good at teaching me this which basically means clear the screen, make the screen black and paint a line in blue that goes from top to bottom, endlessly. If you look in there in the binary, this looks like that. Back then, we cared about every byte and we tried to figure out can we write this with less code so that the assembler produces a smaller binary which is a really crazy thought in 2018 when you can get binaries that do nothing else than print hello world to STD out and take 120 megabytes to do that. So, kind of glad that I grew up in that era. But software development is not about human-machine communication it's about human-human communication. It's about working in a team, sharing the same language to discuss the domain, to discuss the problems that you're trying to solve and express that in code so that you can still understand it later on. And concepts like that are rather new. They became popular like some 10 years ago with the blue DDD book. Yeah, so, I know a lot more about writing software now than I did 17, 18 years ago when I started to work on PHP unit. There's a lot of legacy code that I'm not proud of in PHP unit but it still works, it's still valuable, slowly trying to clean that up and hopefully at some point it will all be cleaned up better but as you all know, you'll probably never get there to make it better every day. So at least I'm not alone with that. Kent Beck and Erich Gamma were in a similar situation or are in a similar situation, the creators of JUnit. In an interview 2006 Kent Beck said, well, the problem in software design is that often the consequences of your decisions don't become apparent for years. So they wrote the first version of JUnit while they were on the plane from Zurich to Atlanta to attend the conference. And they were so enthusiastic about what they created on the plane that they immediately after landing shared it with everyone at the conference and within a couple of days they realized that the architecture and the design of what they built wasn't as great as it could have been but everybody was already using it and they didn't want to lose the momentum by releasing version 2.0 that completely changed all the APIs and how you used it. So they waited a couple of years and eventually cleaned it up. So back to the dungeon. I opened the door. I'll take you on a journey into the dark corners of PHP JUnit now. Here be dragons. So this is a typical project structure where you have your tests. If you execute PHP JUnit, PHP JUnit will search for your tests and run your tests. Quick question, where are our tests? In the test folder, yeah, and more specifically inside the test folder? In example test.php, yes. Right? That was easy. It's unfortunately not that easy for PHP JUnit because of legacy, because of PHP JUnit started, started to work on PHP JUnit in a time where PHP 4 was the predominant version of the language, where most code was procedural, used a lot of global state, lots of global variables, that cost me to add layers upon layers of workarounds for trying to test bad code. That's why it's so complicated. What do I mean by that? So what PHP JUnit does behind the scenes is it recursively iterates over one directory or multiple directories that you specify, either on the command line or in your PHP JUnit XML configuration file. It goes looking for all the test.php files. For each such file, it requires the file, meaning loading it. Since we are requiring it inside the scope of a method that belongs to PHP JUnit, any variable that might be global in the source code file that is included becomes a local variable. So we need to promote those local variables to global variables. I really hope that it's no longer necessary to do that in 2018, but for backwards compatibility that is still there and it needs to change at some point because hopefully nobody writes code like that anymore. Then we need to look for classes that do not exist now that existed before and process them. Why do we need to do that? Because not everybody follows the conventions like the convention to only have one class in one source code file and also doesn't follow the convention that if you have a full test.php file that the class in there is actually called full test. It could be something completely different. So workarounds. For each public method of such a class prefixed with tests, create an object of the class for this test method, collect all these test objects and then eventually and we'll get to that in a bit, execute these tests. In reality it's even more complicated but I'm not going into details there because things like data providers, they blow this up completely. So hopefully at some point that will be cleaned up. So that was more work than expected. But still that should not be that hard to implement and there should not be that much code required to implement what I just showed. Well turns out it requires quite a lot more code than expected. In preparation for this presentation I tried to count it and I think it's about 1500 to 2000 lines of really complicated code to do that because it's 17 years of layers of workarounds upon workarounds that really needs to be cleaned up. And that's called accidental complexity. It's not the natural complexity, the complexity that you have to have in your solution to solve a problem because the problem has a certain complexity. The solution for a problem cannot be simpler than the problem you're trying to solve. But this is accidental complexity, also sometimes called artificial complexity. But if we look at the structure of the PHP unit project there's an SRC directory as you would expect in a test directory, of course PHP unit is tested. So if we zoom into the SRC directory we see the framework directory and the utilities directory and the test runner directory and there's code scattered throughout the code base that does what I explained earlier. So it's really hard to find the right spot when there's something wrong with finding the test because it could be in three or four different places. Accidental complexity, legacy help. Most of it is found in test runner but unfortunately not everything is there. So it didn't get any easier with recent features that were added but at least these new features there are now cleanly implemented in separate classes like test suite sorter, but more on that in a little bit. Yeah, and lots of utility functionality and it's not it's even worse because there's code dependencies that lie in the directory that are being used things like PHP file iterator and so on. So anyway we found the tests. Hopefully now it gets easier when we get to execute them. Okay. So this is I'm not really good at drawing things so this is supposed to be a UML sequence diagram. So when you start PHP unit the first code that is being executed comes from PHP unit text UI command which is like the CLI frontend that does things like read the XML configuration file parse the command line options and arguments and so on. And then at some point hence over control to the test runner which goes on finding the tests what I showed earlier. The tests are currently aggregated in a test suite object. The test suite object has a run method. Okay. So the tests are in the test suite object should be easy enough and that should be all that there is to it to call the method run on the test suite object. Unfortunately not. So test suite will call run on each test case object which and here is where it gets really confusing. Confuses me every time I need to work on this. Test case run calls a method named run test result object. And if you found that confusing it gets even more confusing because test result calls back to test case and calls run bear. Which calls run test on the same object. Then it gets let's confusing because now the setup method on our test case class is called the test method that we're currently executing is called after the test has finished before. Then the test actually calls the code on method is called and we're done. This is how JUnit3 worked. Which I and I copied the design and architecture of that test runner when I implemented PHP unit originally. Okay. I hope you are as confused as I am. I would really rather sooner than later get rid of most of that complexity and have something simpler. And for the longest time there was this ticket number 10 on GitHub. I opened that ticket in 2010. Like over eight years ago. But it existed before that on svm.phpunit.de back when I was hosting when PHP unit development was hosted in a track subversion repository. So I think it's 15 years old now this ticket. And it has things in it that you would expect after seeing this. Like make it simpler. Move test execution logic out of the test result object because it has nothing to do there. Delay creation of test case objects so they don't exist for the entire life cycle of the test execution. Allow for running tests in random order. Resolve dependencies if you have and on each other. Come up with a better architecture for executing tests in isolated PHP processes. Maybe even go into parallel test execution and distributed test execution. And for a really long time I thought none of this would be possible without completely rewriting everything from scratch. And coming back to the idea of the dungeon master and the knowledge in the form of accidental complexity starts accumulating in the head of the dungeon master in the case of PHP unit in my head and silently grows. And less and less people or actually nobody ever wanted to contribute to this core, this test execution core of PHP unit because nobody could understand it. And I don't blame them. Most of the time I don't even understand it. It takes me every time a couple of hours before I can come back to it. So that needs to go. And then something really nice happened. One morning this year in April, I woke up in the morning and after breakfast I turned on the computers, looked into my email, I got the usual 20, 30 emails from GitHub overnight. One of them was a pull request. Pull request from eval that I mentioned earlier. Add functionality to test dependencies by running order. That's interesting. But yeah, I'll look at that later because it's probably not going to work anyway because for that to really work and to be implemented in a nice and clean way I would have to rewrite everything. So I was really skeptical that somebody else who never contributed to PHP unit before would have figured out a way to do this properly. So I thought I was wrong and I regret not having looked at it earlier than I did. So what he wrote in a ticket was something along the lines. I ran into this problem. I found ticket number 10. So I realized that you are interested in this kind of change. So I worked on that. And I did start working on this thing. So I started at my company and it has proven to work in our daily business. So every developer runs this when they run that test locally. We do it on the continuous integration environment. So it really works. And here is the current state of what it looks like. And please tell me what I need to change to make the pull request possible. So I started working on this style issues and then eventually that was merged. And just merging that one pull request which went into PHP unit 7.2 solved a couple of issues from that infamous ticket number 10. Sorting tests according to the depends annotation solved problem. Allowing to run tests when that was merged he started working on the next thing. Like when you run the tests run those tests first that failed the last time. Eventually that will be expanded to first run those tests that failed the last time and that were added since the last test run. Now that we have an infrastructure for that, all of these features are really easy to implement now. So you run the tests first that are the fastest and run the tests that are the slowest at the very end. So as I mentioned earlier he only started contributing this year. He's already the top four contributor of all time. That's really great. And then the next thing we're going to do is run the tests. So he's the top four contributor of all time. That's really great. And he isn't happy with just adding new features and cleaning up stuff. He's also fixing bugs, helping people on bug reports. So that's really, really great. So he was able to do what I thought was not possible without rewriting everything from scratch. That's really great. So preparing for this presentation today made me realize that I need to write down the ideas that I've had for quite some time and write them down somewhere. There's a new ticket that supersedes ticket number 10. It has a lot of information that was previously never written down of how PHP actually internally works right now and evoud and others are now going step-by-step through the things that are not great and try to make them better incrementally. So that's cool. Because at the end of the day, the problem we're trying to solve here in the test runner is rather simple. We need to find the tests, filter the tests that we're interested in running right now. Of course, by default, that will take place in tests that deal with a specific issue or the test of a specific class or a specific module, but still filtering needs to be done. Then, and that's rather new, the tests need to be sorted. Do I have a sorting criteria? Do I need to resolve dependencies? Do I need to sort? And then execute. And as I mentioned earlier, at the moment, this is somewhere around 1500 lines of code that we're using. It's still really hard to understand. So if I really wanted to start from scratch with the test runner, this is probably what this would look like. Yeah. No worries. I'm not going into too much code and technical details because that's not, it's supposed to be a keynote. That's not supposed to be technical. So what do we need to do to find tests? Well, let's write a simple object called a test finder that takes as input a list of directories and returns a collection of test objects. We still have to do all the work that we're currently doing, but that will be encapsulated in there and it doesn't bleed out into other areas of the code base where it does not belong. It's a really nice abstraction. Now, 18 years or 17 years after I started to work on PHP unit, I have there are more libraries available that I can build upon. So I don't need to implement all that myself. And maybe I don't even want to include the files and use reflection to look for tests. So what I really would like to do is rather analyze the test code statically and back then that wasn't possible. Now I have things like better reflection and PHP parser that can do that for me. I also don't really want to care about traversing the directories myself. Symphony finder does that. Five lines of code done. Yes, there's a lot of code in there. That's in vendor. It's not my code. I'm not responsible for that. I can reuse that. I can use better reflection to analyze the source code and say hey, here's a file. Please give me all the classes in there and I can iterate over all the classes. Look at each method in that class. Figure out whether or not that is a test. And then just create a simple object that is not the real test object but a value object that represents. There's a test with this name in that class, in that source code file. And then the executor can be really simple and just execute that. So this proof of concept code is really easy to understand. It's less than a 100 lines of code. And if you spend a couple of minutes looking at it, I'm sure you'll figure out where you would have to extend it to implement the feature that you're interested in. This proof of concept code is also easy to test, maintain and extend to have. But why is that? Well, to a large degree workarounds for procedural code and global state have been removed. It's also not feature complete. There's no set up tear down. There's no test dependencies yet. All of these, but all of those can be added really simple by extending at the right place. Most importantly though, the concerns of finding and executing tests are clearly separated. They were not separated before. Code follows ideas of domain driven design and clean code principles. So it really communicates its intent and it's cleanly implemented so it can be tested. But even if that were the state that PHP unit is in right now, which it is not, even with clean code new developers need to learn how to contribute to PHP unit. That's something that I started to do something about last year. Last year I started to do code sprints. A PHP unit code sprint is a two-day event, usually on a Friday and Saturday, where 10, 20 people come together that want to learn how to contribute to PHP unit. And every time, every single time they come together. Last fall in Tüsseldorf earlier this year in Hamburg three weeks ago in Munich next one will be in Würzburg next year. One after that, I don't know. I have offers from companies all around Europe at the moment that want to host the next one after Würzburg. It might be in Milan, somewhere in Germany again. I don't know. Maybe at some point we'll have a code sprint in Asia. If you're interested in that, contact me. So the nice thing of these code sprints is that every single code sprint was a huge success so far. The people liked it. The people enjoyed learning about things and contributing because they use PHP unit every day and all of them came with that one bug that kept biting them in their daily work and they were not really happy with that but they didn't know how to fix it. So I coached them, taught them how to fix it and they did that. Some came with great ideas for new features. So at the code sprint three weeks ago Sebastian Hoya implemented functionality inside PHP unit that leverages functionality in XD bucket. Derek recently added to make code coverage faster. So that will be released next week, Friday of next week and then code coverage or run executing tests with collecting code coverage and processing code coverage and reporting that will be around 50% faster. Which is quite nice. In some cases it was even twice as fast. It depends on how much code you have to use. So that's definitely something useful. At one of the code sprints last year there were two attendees who use PHP unit on Windows and have over the years run into several issues that were window specific. I gave up on working on Windows a long time ago so I don't test PHP on Windows, PHP unit on Windows. So Travis doesn't offer continuous integration on Windows. I knew about something like Travis for Windows, AppVayer but I didn't have time to figure out how that works and to be honest I wasn't really motivated enough to figure that out because Windows. But there were two attendees at the code sprint who care about Windows and who had experience with AppVayer. So they spent the code sprint on AppVayer for PHP unit. So that was nice. Others contributed to the documentation or looked through the issue tracker and tried to reproduce open bugs and provided feedback. Yes this is a real bug, no this is using PHP unit in the wrong way. Not a bug here. Read this part of the documentation to figure out what's wrong. Okay. So that's what I'm trying to do over the last couple of years. Changing version control to GitHub helped a lot to lower the barrier of entry. Cleaning up the code helped a lot. One of the biggest things that I've done over the last couple of years is make PHP units smaller. Like take functionality that was in the big monolith that PHP units started as being and take that out into a separate component that others can use and these new components they are clean code. They follow good design principles. They are easy to test, easy to understand and in those dependencies of PHP unit for quite some time there are many active contributors but only PHP unit itself is still this big hard to understand monolith thing. Code sprints to educate people on how to contribute to PHP unit and how could a new test runner look like. It's probably unlikely that there will be a big bang release that has a completely new test runner that would be very scary at least to me because I do not want to release something new like a PHP unit 8 or PHP unit 9 that is not able to run existing tests. There has to be 100% compatibility for that. So that's why we're probably changing from minor version to minor version making it a little bit better from version to version instead of one big bang thing. Yes, this will take us a couple of years but it's still fun and if you want to have a part in the fun come to a code sprint learn how to contribute. Thank you. Is there any questions? Hi, I wanted to ask is there any way to remotely attend the code sprint? Is there a way to remotely attend the code sprint? We've had remote attendees in the past like one or two remote attendees so one of the things that we do when we have a code sprint is there's a Gitter chat on Gitter.im where all the onsite participants are in and discuss and like I'm currently working on that issue if somebody wants to help me or ping me Sebastian I need your help can you come and look at this? And we've had people at the two most recent code sprints participate remotely but they were attendee participants that had been there in person at previous ones but I'm short answer yes that probably is possible I just need to announce that and put it on the information page that that's the code sprint date and if you cannot come then you can participate remotely and just come to the chat I don't know how well that works with regard to coaching if you participate remotely I cannot sit down next to you and look at your code and try to fix that but yeah? Just curious how do you run effective code sprints probably like besides you to overlook how the new developers do coding you probably may meet probably a few more new tenants to help you look after So how do you run an effective code sprint? I don't know four times now I'm very happy with the results that I got so there is some effectiveness there could it be better? Maybe we are learning as we go people that contributed for the first time to PHP unit at the first code sprint they mentored other people at subsequent code sprints so they helped me mentor new people so that works, that scales a bit so what we use so the general agenda for code sprint is that on the first day for like half an hour or an hour I give an overview of where to look in the code for specific things I usually pick a couple of issues from the issue tracker that are good candidates to look at as your first contribution or good candidates for learning about certain things about PHP unit like if you are interested in logging test results to XML here is an open ticket look at that while researching while working on that you will come in contact with these areas of PHP unit and then people will take what they want to work on and ask me when they have questions and then when they have implemented something like a bug fix or a new feature, they send the pull request and then as a group at the end of the day we look at the pull requests and discuss what was going on in the pull request and then it's merged usually I hope that answered your question Hi how do you encourage developers to write unit tests because usually if it's already working developers get lazy to write tests I think there's more than one question in there so very subjective answer from my perspective I do not trust code that doesn't have tests and that includes code that I have just written like if I were to write code right now for five minutes I wouldn't trust that until test tests so I don't trust myself to write code that doesn't have bugs that's why I need tests and I don't believe that anyone can write code that is bug free without some sort of testing and some sort of testing for a lot of people means manual testing I open it up in the browser yeah it works or maybe have some bar dump or print R or echo in my code okay I get the result that I need or that I expected and then I remove that line of code and continue on writing tests writing automated tests is not really something completely new or different from what you're probably already doing you're just doing it in a more structured more formalized way and keep these tests these debug statements that you currently have and put them somewhere outside the code and you can repeatedly run them so you're not only verify with them that it works right now in the second that you have written the new code but you can rerun them in an hour, tomorrow, next week, next year to verify that it still works yes it takes some time to learn the tool and to learn how to write good tests but at some point you have mastered that and it becomes an indistinguishable activity from writing production code so again very personal statement I can no longer after years of doing this I can no longer distinguish between writing test code and production code yes I know I'm currently in a test case class and I'm writing test code and I'm now outside a test class and I write production code but thinking about that thinking about the problem that I want to solve and thinking about the solution I'm implementing that includes both the production code and the test code and I it's inseparable again very personal thing do you think one last question anymore? apparently not that's a good question at the brick thank you Sebastian