 here to talk about refactoring. What is refactoring? A lot of you probably already know, but just as a recap, it's kind of restructuring or reorganizing or re-architecting your code and your files and your data structures and your directory structures and so on. So this could be like breaking long functions into smaller functions, moving code between files or between modules, renaming functions, things in general, moving files and directories around. But the important thing is it doesn't change the external behavior. So when you finish your refactoring, it should still work in the same way. So external behavior, in this case, could be like a web application. It could be, if you're writing a library, it could be your public API. That should stay the same. It's important because it improves readability of your code. It reduces the complexity of your code. And it's more maintainable, which means when you need to add a new feature or something, then it takes less time than it would if you got this massive chunk of code that takes you a long time to get into what's going on there. And most importantly, it's going to be a happier team. And future use is going to be a lot happier as well. That's an important person. So in general, the process of refactoring, the most important thing is to have tests in place. Now, when I say this, a lot of people assume that means automated tests. It doesn't. Automated tests are better because they're easier to repeat. But in general, if you get a process of steps that you can go through and repeat, then that will work as well. And what you want to do is make small incremental changes to your code. And then after each little change, you want to run your tests. If the test passed, then keep going. If the test don't pass, you've done something wrong, and you want to go back and change it. So in Elixir, I would argue that refactoring is maybe easier than in a language like Ruby or JavaScript, but probably not as easy as a language like Haskell or Elm, where the compiler gives you a lot of warnings early. Because all the arguments are past the function, which means when you look at function, you've got everything you need to know what's going on there. It's compiled as well. So if you made a typo, then you'll find out compile time. And this is especially true if you're doing something like working on an embedded device and you need to pull out your SD card, plug it in, and it's like, that didn't work. Take your SD card out again, put it back in your laptop. So you get this at compile time. There's no dependencies in the file path, either. As long as your code file, your module file, is in one of the directories that gets compiled, it can be anywhere. And the test run quickly as well, which if you're going through a test every time you make a code change, it's super important that it's fast. Otherwise, you'll get frustrated. And there's some changes in Elixir 1.3 as well that make this easier. I don't think that was the intent, but as a nice side effect, it does. So there are warnings and imperative assignment. So this means if you set up a variable and then change it inside an if, then it's scoped locally. So you'll get a warning saying you shouldn't do that. And that was a 1.3 change. There's MixedXref as well, which will find functions and modules that don't exist for you. And this runs automatically when your code's compiled. So for example, if you've got a reference to a function that no longer exists because you've moved it, then you'll get a warning there. The compilation time is slightly faster due to the dependency tracking. And there's also X unit diffing, which is a huge change. And it looks something like this. So you'll see this is the output of a test. And the diff between the two strings when using the assert macro, the word lazy here has been removed, nor brown has been added, which is indicated in green for the word brown. OK, Elixir. So let's talk about Elixir and some refactoring techniques you can use. This is my application. It's called Pokemon Go 2.0. And this is what it looks like before refactoring. And this is what it looks like after refactoring. So the point about not changing your external behavior, that's it right there. So, oh, speaker notes. So the application looks something like this. Nope, this. So I've got these two web browsers here. By the way, the projector in here runs 720p, not 1080, which means that the windows are tiny, but it will make do. So the way the game works is you go into the home page and you can view the challenges. And there's currently no challenges, so you can create a challenge. And then it goes into this waiting state where it's waiting for another player to join. And you can refresh that, and then you'll just wait in the waiting state. And then in the other window, you can come along and view the challenges. So hey, I'll join a game with example user. He sounds like a cool guy. So you join the game, and both of them have updated, even though it's here. And you'll see that the current turn is x. So x always goes first, but the person who's x and the person who's not. This is tic-tac-toe, by the way. We call it knots and crosses. I'll use the two terms interchangeably. But the player on the right here is the x's and the player on the right's the knots. So you can't play when it's not your turn, because that would be weird. You can play when it is your turn, but you can't override an existing piece when it's your turn. Play continues. It's like a battle to the death until one person is declared the victor. And they get a sweet message saying you won, and then the other person lost. So that's it. I had to disable my Wi-Fi as well, so unfortunately I can't let you play once I'm doing this. But that's it. So the winning criteria, if you've never played this game before, then that's weird. But it's one of the classic board games. But just in case, you need three matching pieces in a row. There's three horizontal wind lines, three vertical wind lines, two diagonals. And if all the spaces are filled and none of the above criteria are matched, then the game's a draw. So in code, it looks a bit like this. I apologize if the code's quite small again, projector. But the way the code works here is I've got a condom there and it says, so I'm destructuring the list, which is how the board's represented. There's nine elements in the list. I'll get onto why I'm using the list later. And then three conditions. So I've got the three horizontal lines represented first. So I'm saying if A is equal to B and B is equal to C and it's not nil because that's the default, then that means there's a piece there and someone's won, the piece A in this case. And then it's kind of okay for the horizontal wind line. You can kind of guess, okay, D is equal to E and E is equal to F and those letters come next to each other, so they're probably next to each other. And then you've got the three verticals. You've got A, D and G. And then you've got the diagonals and it gets quite confusing. And that's deliberate because it's a refactoring talk. So, and then I was saying like, if any of the pieces are nil at all, then the game's still in play and return false because that obviously means the game's still in play. And for any other clause, which is the true, it's a draw. So all the pieces have been filled and no one wins. So you come across this piece of code, your first day at the Pokemon Go 2.0 headquarters and you're like, who wrote this code? Get blame. And it turns out it's this guy. And I use a scientific technique here called defactoring which is when you take a piece of code and make it worse. The whole commit message here is actually defactor, make game server function awful. And that's why I did. So we can refact this, right? So we can use a technique that's called if to pattern matching. It's fairly descriptive about what it does. So pattern matching is one of the core concepts in Erlang and Elixir. So let's take a look at how we can do this. So instead of having all these ifs and saying it's A is equal to B and B is equal to C, we can have pattern matching. So we're pattern matching on the list and here we see the first three elements match and it's not nil, unfortunately, I have to put that in. Then the piece is one. Same for the second horizontal line and the third and then the three verticals and then two diagonals. And it's a little bit more readable and we still got the draw case at the end. It's more idiomatic now because we're using in so we're saying if nil is in the board then the game's still in progress otherwise it's a draw. I showed this to someone and they're like ah, it doesn't seem more readable and I went okay, fine. There's a side by side comparison. So you see there's no like, you don't have to know what A, B and C are or G. But you can also do this, which makes it a lot clearer. So you can, you know, like you can pattern match across multiple lines. You can define your function over multiple lines. And here like the mat, the layout of the pattern match here is the layout of the grid and you can clearly see that we're looking at these three pieces. So you'll see that technique as well and like maybe people making a poker game or something and you wanna check that the suits match. You'll see a similar technique. So that's if to pattern matching. It's one of the more common refactoring techniques. Use multiple function heads and in my last example the layouts match the data structure. Excuse me. So let's talk about the application. The way the application is structured is there's challenges that are stored in the database and that's when I clicked on view challenge, great challenge, they get stored in the database. There's a game server, which is a gen server that holds the state of the game, which is like the players who are in the game, the state of the board, whose turn it is, whether like the game's in progress or not. And then there's like a registry that maps the IDs of the challenges to the PIDs for the gen servers. We probably won't look into the code in that, but that's what it does. And then all the communications handled over Phoenix channels. So here is the gen server. So there's three functions here, start link, which is a wrapper around gen server start link. It's pretty common in gen servers. We have a join function, which joins the game. And then we have a play function, which plays the game. This is like the public API. And then we have the init function. So here you'll see that it returns okay. And then the entire state, which at the moment is a map. The board is a list of nine elements, which I use the list because later on I was gonna talk about how we could change that to a better data structure, like a tuple. I guess if there's like a tuple with three elements, I'd like to call it a triple. So I guess a nine element tuple would be a nipple. So the player's an empty list. The state is waiting, as I said, the X is all start first, so X terms true. There's no winner and there's no win line. So the function is deliberately like obfuscated. So the first clause is saying when there's less than two users in the room, then check that the user's already there. And if they are, that's fine, just return state. And that's why refresh works. If there's like a new player, then prepend them for the list. If they're the only player there, then they go into a waiting state. If both players go in, we shuffle the list of two elements and that's how we randomize who places which marker. And the game goes into a started state. And then we update the state and return that state. And then we got the second clause here for the join as well, which says if the user's in the game, that's fine and they can play. Otherwise, the game's full and they've got no business being there. Like if you've ever tried to play this game with three people, you got like the knots, the crosses, and the triangles, it doesn't work. So the other thing we got was play. So again, we check if it's a valid move and that means it's within the bounds of the grid. Like you can't place a piece, a negative index. You can't play a piece that's further on. And you also can't override an existing piece. I'll show that function in a minute. And then we say if it's the user's turn, so the way we do that is if they're the first player and it's the exit turn, then we return okay, zero. I'm using zero and one here to represent the knots and the crosses because it's a talk about refactoring. What you should actually do in this case, maybe use an atom for like a knot on a cross that'll be way easier to read. So it's just a little thing I put in there. Otherwise it's not your turn and you can't play. Then we take the result of that and we pipe it through a case and then we work at which piece it is. We update the board using list.replaceaxe, using a list. And then we update the state, we check if the game's won using game one function I showed earlier. And then we update the state. If there is no winner yet, then we just invert the X turn and it's the other person's turn. If it's a draw, then the game's finished and there's no winner and if it's ending else. X is a terrible variable to use here because that's got context to this application, but that's whoever wants. That should be the winner. And they get assigned as the winner and then the line gets set so it can be highlighted in the front end. And then ending else will return an error. And here's the valid move function. So if the index is less than zero, then it's an invalid move. And if, so we're using enum.act here, which means that it has to be within the bounds of the list. And instead of returning nil, which is the default value, because that's the default value of the board, we're returning invalid, which means that nil is true and ending else is false, which is, again, deliberately confusing. And here's the game one function that we got earlier. So why refactor this, right? It totally works, but it's difficult to read. Like I wrote the code and I was struggling there. There's some unnecessary duplication. I don't want you to think like, all duplication's bad, that's not true. There was a case of left pad in NPM where some duplication would have been good. So a little duplication's bad and a little dependency. But it takes some time to understand and get in the zone of what this code's doing. So next thing we can do is a technical case to function. And what we do here is we're looking out for nested cases. And this is something that a lot of beginners to the language do. They'll put like just cases, because it's easy. And that's fine, but there are different ways you can approach this. So it helps prevent this cases and removes like horizontal code in favor of vertical code with like named functions. And the functions have names, so it's more descriptive of the intent. So this piece of code here for the join has a nested case. If you can't see it, it's this case here, which is inside an if. So what we can do is just move this to a function called check all players present. And now we don't have a nested case. We have a function call instead. The function looks exactly the same as the case did before. It returns a tuple with the players that are in the game and the state of the game now. So that was it, that was all there was to it. So the next thing we can do is move the callback code to functions. So this is a similar technique. When I'm writing a gen server, I like to keep my callbacks pretty small and pass off to some helper function. And the same goes for like a Phoenix controller. You don't want like lots of logic in your controller actions. You want to move out to helper function. So it's more descriptive because they're named again. It's very similar to last factor, but the test should still pass because, so the test was gen server, I forgot to mention, are a little bit complicated because the public API is so small and you don't know who's playing first. So you need to work out who's turn it is by trying to join and it'll tell you who's turn it is. And then you need to, based on who's turn it is, send different play commands and work out all the states for all the wind lines and all the draw conditions, which is a little confusing, but it works, so the tests are there so I'll probably keep them. So let's look at the join function again. So we've got this clause here and instead of having the entire body like we've got here, we can call a join user function. So this is still the clause when there's less than two users and the join user's like the helper function. So it updates the state. So we're passing the whole state object and I'll get onto why we're doing that shortly. But it's quite a common technique for state transformations to pass the entire state through. So here's the join user function. So again, if the user's there, that's fine, return the state. Otherwise update the game players. So I've renamed this function, which used to be called check all players present to update game players. And that's because now it returns an entirely new state. And if we look back up here, you'll see that the state is replied to here. You'll see that's what gets responded to the web socket. There's still these two clauses though. We can consolidate these down into one clause that looks something like this. So because if you're there, then it returns okay in the state. And because if you get added, it returns okay in the state. So we can match on the join user function and check for the state. And if it's okay state, then we'll update the state and return the new state. Otherwise we'll keep the existing state and reply with whatever was returned, which is probably gonna be an error. And the join user function now is makes heavy use of pattern matching. So the first clause says if there's one user in the game and it's me, then that's cool and return the state. The second clause says if there's two players in the game and I'm either player one or player two, then that's fine return the state. You'll probably notice here that instead of saying when user ID in players, I've got to say when user ID in P1, P2, this is because guards need to be available at compile time. So I need to build it manually. And that's why I use that. In the next one, we call our update game players function as we did before. And for anything else, the game's full. You can not play. So if you look at the diff so far for the join function, we've got the, this is what it looked like before, where it was just everything was in the handle calls. This is what it looks like now. So it's not actually shorter, but refactoring is not about writing shorter codes, about writing more declarative code and more understandable code. So up next is the play function. So this will use similar techniques to the join function. So I'm not gonna go through each function in detail, but we're refactoring into functions and then we'll go on to something different. So here's what it looked like before. If you remember, I'm not gonna go through it again because it took forever. But it now looks like this. So we've got one function. It looks exactly the same as the join. We've got some case and then we do something, whether if it's okay, otherwise we return the error. And then we've got this update game state function, which updates the game state. Go to the valve move function, which I had before. Then we've got the player's turn, which checks whether, you know, your player one is the X turns and so on. The play turn function takes a state and it places the marker and then updates the game win state based on if someone's one or not or if it's a draw. The place marker updates the board and the game win state just checks if there's a winner and returns them. So let's talk about case to pipeline. So this was pretty common pre-Elexer 1.2 and what you would do is you wanted your function to read the series of transformations. If you can't spot it, look out for the pipeline operator. So we've got this function here, which looks a bit ugly because we're, for some reason, piping into a case and we could just call case in the function, but let's ignore that for now. And instead of writing it like this, we can write this, so we can say state and then check if the move is valid, check if it's the player's turn and then play the turn. The problem with this is that we need to know what's happening with the response so we can no longer just return what we want. We have to wrap it in some way. So we start by returning okay from the valid move instead of making a boolean and then in each function here we have to match and say if it's an error, return the error and if it's okay, something, then that's fine and we'll use the state. Same here, if it's an error, return the error. So we have to constantly tag our values whether it's an error or not an error. And what we can do instead of this, so this was pre-1.2 and it was a big problem and there was lots of posts on the mailing list about it and everyone was writing their own monad libraries, there's about 14 monad libraries on hex. Don't use any of them. You can actually, I'm not here to tell you what to do. So the case to pipeline though, so there's a built-in solution. So the problem with case to pipelines, it makes the first function look nice but you have to modify all your functions and their return values. Instead, we can use something that was introduced in elixir 1.2. It's probably my favorite thing that's been introduced to language. It's called width, I use it all the time. It removes the need to change your function heads but it still reads the series of transformations which you'll see in a minute. It enforces matching at each step which will make more sense in a second but you can also assign inside the width. So here's our pipeline and here it is using width. So when I say it has to match, it means that if the valid move doesn't return okay, then the width will stop and it'll return whatever the result of valid move was. Same with okay marker, player's turn doesn't return okay marker, then it'll return whatever the result was. And then here we're using an assignment here so we're saying the new state is this and then do sort of return the new state. And then in the case, if we get okay state, then that's fine and we'll return that. If we get anything else, we'll return whatever was returned. So when I say it still reads as a pipeline, as a series of transformations, it does, you just have to shift your eyes about 20 characters to the right and then that works. But there's still a problem here which was fixed in elixir 1.3 which is this part here. So piping into case is, it's a little bit ugly but it's quite a common pattern when using width. But what we can do instead is elixir 1.3 introduce with else. So we can now say with something do and that's the positive path else and then we match, it matches the same like a case would match. So we say for anything else return that result and that's the whole function there. So that's kind of all the functions looking nice now but they're still using a map for the state and what we can do instead is just struct which is a construct that's built on top of maps so it's a map with an underscore, underscore struct, underscore, underscore key in it that points to module. You can match on structs as well so you can say only this struct is valid for this function. Ensures the correct types as well and that's especially true for using something like dialyzer then you can specify types to your modules. So you get the compile time guarantees and you can implement protocols on top of them which I don't go into in this talk but the gist is that maybe we'd wanna implement the inspect protocol for our game and then when you output to the terminal it shows us like an ASCII version of the grid should've done that thing. So here's what a struct looks like. You call def struct inside your module and the contents here have just copied from the map so it looks exactly the same except it's now structured and we now can return the default game state in our init function instead of having the whole map in there and then each function matches for that particular struct which is a game struct. If you try and call this function with anything else then you'll get a match error and I've just updated that all the way through the file. So now we have this module that we can use for our struct so we can move all the functions that we've defined now over to that module and that means our code and data together which is really useful because you can see what you can change. It's easier to test as well because you've got all these functions now so before we had to set up the gen serve in a particular state which you can also do with sys.getState and updateState or writeState but I had to play the game for the test to work whereas now I can test my edge cases in this particular module and you can module.false or .false particular functions you don't want people to depend on and you can still test them and that's quite a good technique. So here I've just moved all the functions over so this is now the game module and all the functions exist in there and then here instead of just calling the local join user function I'm calling game.joinUser or game.updateGameState. Okay so that's elixir. The techniques are fairly applicable to any library you're using so that's why it took so long. We'll talk about Ecto now. So a common thing in Ecto is counting so this is what it would look like prior to Ecto 2 a lot of people writing code like this and what we're doing here is counting the challenges for a particular user so we get the user we get their associated challenges and then we use a fragment to get the count and then use repo one to ensure one results returned and I used to see a lot of people so I picked my things that are discussed based on like what comes up all the time on mailing lists or Stack Overflow and what you can do in Ecto 2 is use repo to aggregate which is really nice it takes a queryable and it takes some form of aggregation which is like a count or an average or a sum and it takes a field so you can repo to aggregate. So one of the things that we do in this game is we fetch the open challenges for a user and then we increment a field on the users called refuse challenge count under the assumption that like they're so good that no one wants to take them on anymore because if you've ever played this game you'll know that after like four games all games end in a draw and then we'll remove the challenges from the database because we don't need them anymore we need to stop all the processes for the games that are run because we start the process when you join the game as the first player so we can stop those now and remove the pins from the registry so we can use a new feature in Ecto 2 called Ecto Multi for this so it's kind of like a pipeline but for your database transactions but you can also run arbitrary functions in here which I've got an example of so currently we use a transaction so we have this repo transaction on line seven and we're using with now because I've shown it so I can use it so we say update the user and then if the user updates successfully then delete all the challenges and if the challenges are deleted successfully then stop the games and the reason we're doing this order is because if we like delete the challenges but the games don't stop then we no longer have a reference of which challenges existed so we'll never be able to stop the games for that and anything else we use repo.rollback which rolls back the entire transaction so here's the functions this one negates the age to get all the old challenges because it's not like a daytime subtract function Ecto is only daytime add if you add a negative value that's the same subtraction so we get all the challenges that are older in a certain time period and fetch them the delete challenges composes a query for all the challenge IDs that we have and calls delete all on them the stop game maps over the game registry calling delete game on each element and then the update is using a change set and updates the refuse challenges with the count that we pass through so what we can do instead is use an Ecto multi so you start with Ecto multi new and that starts multi-struct and then each time you add a function you give it a name and when Ecto multi returns it returns a map of the name and the particular value that was returned when you execute that function so instead of updating the user directly we pass a change set and then that will be called to the Ecto multi update same deletes instead of deleting the users directly we call delete challenge query which returns a query which then gets passed the delete all here's a function so the argument that gets passed that we're ignoring is the state of the multi this far when it's executing and we're calling stop games but we're ignoring the argument and then you can pass that to it you pipe it through to repo.transaction so these functions now return queries instead of actually executing and the other thing is for a multi to succeed and each return okay something so that's why we're returning okay something and not just okay so Ecto multi returns a data structure that you can pass into transaction but that's not the only thing you can do with it it's quite easy to test as well because you can pipe into two lists and then perform assertions on it so that's useful for like testing it in isolation they get executed in order from top to bottom so if anything fails then it'll stop executing the entire multi pipeline and it won't run if you got change set errors as well so it doesn't even need to go to the database to fail it can fail if you got a change set validation error so that was Ecto multi let's talk about Phoenix so what was that so the Phoenix generators are as we always say a learning tool so they're fast to get up and running and by default this is what will happen so if your get from the repo returns like can't be found then it'll show 404 which by default looks like the bottom right picture there and if you got 500 errors they say you're using UUIDs and you pass a non-UUID then you'll get an internal server error so what we want to do instead you can customize these pages by the way but that's what they look like by default so what we want to do instead is if you can't find or there's a 500 then we want to show flash message and redirect the user to like the list of challenges probably show you what that looks like looks like this yeah looks like that so that's an error but when it doesn't raise an error it'll be fine oh actually I'm using a different branch that way so what we want to do is override the action so this is something you can do in Phoenix controllers the action is called at the end of the pipeline and by default it calls apply with the action name for the controller and it passes in the con and con.params so here is the generated controller I've omitted the index creating new actions because they're not relevant for this particular example but you'll see that this line here appears over and over again in each function and that's the thing that will raise that will trigger the 400 or the 500 error what we can do instead is we can override action it's def overwriteable in Phoenix controller so here I'm assigning the action name to act name because both action and action name are taken by Phoenix controllers you have to come up with something new I'm using with again and I'm saying if the action is either show edit update or delete then find the resource and if it successfully finds the resource then we call apply on the module with the action name but we pass this additional third argument which is the challenge else so this is matching on the else of a width if it's false then we apply with just the two arguments so this will be true for index creating new and if we get error not found then we'll put a flash message saying not found redirect back to the challenge path and then halt so find the resource function looks like this so we're saying if the UID is castable then it's about UID and we'll continue and then if we can find the challenge then we'll return okay challenge and otherwise we'll turn error not found and that's the thing that matches that triggers the flash message and then in all the actions here so instead of calling like the repo.get inside the controller we can have this third argument here challenge and that's passed through to each of them so yeah so overriding action allows for explicit parameters in your actions they can come from anywhere as well so you can also define a custom controller so maybe you've got like a user that you want to use in several controllers you can define your own controller and then use that instead of using my app.web controller and that'll work as well so the other thing we can do instead is like find with a plug so plugs are pretty common in Phoenix like the whole thing is a series of plugs a plug is called before the action we can combine it with the previous technique as well so you can use a combination of the plug and defining an override you can use function plugs or module plugs if you use a module plug then you can use it in multiple places but if you use a function plug then it's got to be local to the controller and I've scrolled there so we call it with plug so we say plug find resource when the action is one of these actions and then our find resource function now looks slightly different we're still doing the with but if it succeeds then we'll assign the challenge on the conduct signs otherwise we'll put the flash message there and then we'll return we'll redirect the challenge pass and they'll see the flash message I know what we can do instead is use conduct signs.challenge so here I actually didn't need to keep this in but I have for demonstration purposes so because it's already on a signs we can just go ahead and call render directly but I'm passing it through explicitly here to show you the difference between the previous version and you'll see that it's in all of these conduct signs.challenge instead and if you were to combine the two techniques you could just check conduct params in your action override and then pass it through as a function argument so there are a couple other things in Phoenix that I should probably mention the first is that plugs can be used in most places you can use them in the endpoint so I've used this before to check if you're on a subdomain or not because you can only have one root to a particular resource in your router in the router as well a common place a common reason you do this is if you want to guard a particular set of roots behind authentication or authorization then you can use a plug in there and then you can use them in controllers as I saw so the other thing that I should mention is you want to try and keep your modules web-focused this is kind of what Chris was talking about yesterday where you want to pass off to some other module in some sort of context or domain that does all your logic that's not web stuff the general technique I use for this is to keep report your controllers so you can just open up your web.ex file delete the alias for repo and then watch everything break and then slowly fix it and this also has the benefit that it allows other interfaces into your domain so the web isn't the only way into your application you could also maybe have a mixed task or some other way to your domain logic so in summary you want to keep your team in future you happy write more maintainable code and reduce the technical debt and yeah, that's it, thank you this is me I'm Gazzler except for Twitter where I was late and I'm the Gazzler I work at a company called VoiceLair to push the talk stuff and the repo is open source and each change is like a commit so you can go through and step through these examples thank you by the way I had a question about so the multi with the non-database interaction point so with the multi if any of the individual commands that are happening there there are database related that would be like within a transaction presumably is that accurate? the whole thing is a transaction so you're piping the whole chain of things you built up into a transaction so if you wanted to have that non-database related thing not apply maybe just always put it as the last piece of the multi you can put it any way you want as long as it returns error something then the multi will stop if it returns okay something then it will continue through the multi but I want my non-database related thing that I put into a multi to also roll back well that depends on what that thing is if you're stopping things in a gen server then after you've killed the paid you can't resuscitate the paid it's gone it's dead so in that case you just have to think about how your application works in that particular case putting the end is one way having some sort of fall back in place or if you can't kill it is another maybe you could persist the state first then if none of them if they don't all die then roll back and keep the state as it was perhaps yeah you're right you need to think about that okay thank you let's wait until we there are examples there where you're talking about going from horizontal to vertical and I've had that same idea but when I put stuff in a function heads the guards end up out in column 60 or 70 so there's got some tension there so I haven't really figured out how to do that right so you got horizontal but not nested so you can write your guards across multiple lines as I did in the first example with the grid and that's fine so maybe you can have the arguments to your function on separate lines and guard against each one individually but you're right that totally happens all the time and you need to find a way to fix it any time I'm unsure about code style I just go to the elixir repository and look at how they do it and spend hours searching for an example and then just copy that does that answer your question? anyone else? I think that's it okay thank you very much