What's up everyone, Takedown here. Welcome back to another video. Today I'm going to show you guys how to factory reset any Samsung Galaxy phone. This is really quick and easy to do and it even is for some of the newer phones as well. Any Samsung Galaxy phone can be reset the same way. It's the same exact method. Now depending on what you have stored on your phone, it might take a lot longer for it to finish processing the factory reset, but stick with me because it's the same exact steps that you are going to take. So the first thing you don't want to do is to go into the settings.  Now here you can do one of two things when you're in your settings. You can scroll all the way to the bottom to general management, and then from there you can go to the bottom of that and select resets. That will take you to the reset screen. Another thing you can do is just in your settings, you can go and search factory, and whenever you search factory, it will bring you to the factory reset menu. From here you can resets various things. You can reset all settings, reset network settings, resets accessibility settings, and factory resets. That is going to be the option you're going to want to choose. If you're factory resetting the entire phone, you're going to choose factory data resets. So this one here, you're going to click it, and it's going to tell you everything that is going to be erased on your phone.  Photos, music, downloaded apps, pictures, everything, even your settings are going to be going back to factory settings.  It's going to be deleted and it cannot be recovered. If you've backed it up to an SD card, that's honestly what I use. I have micro SD card always in my Samsung phones. That's why I choose Samsung over Apple, and with that, I've already backed everything up.  And now if you're signed into your Samsung accounts on your phone, it will ask you before you factory resets to sign in to verify that it actually is you attempting to factory reset this phone. As you can see, I already did that before now, and I already had it factory reset. I'm just showing you guys, and this is on a Samsung Galaxy A50. So here you'll see it. It should be fairly quick because I already factory reset this phone. I'm just doing it again on camera to show you guys how easy it is. Again, it will take likely a lot longer for you guys to factory reset your Samsung. And I know I'm showing you guys on a Samsung Galaxy A50, which I believe was released in 2017, this model. It can be worked on any Samsung Galaxy phone. Doing this method, just going through, searching factory will bring into the factory settings, and then you just factory reset your phone. Again, you might have to sign into it to verify that it's you, but it shouldn't take no time at all. Just remember before factory resetting to go and back everything up. I always save my music because that's what I usually have the most of on my phone. And any videos that I have for YouTube, I'll go and I'll save those on the micro SD card and then I'll just transfer them whenever I do. So I pull out before factory resetting always pull out my micro SD card. That way I know 100% nothing on my micro SD card is going to be erased. Now, even though this is taking a little bit of time to factory reset, just remember if you have a lot of stuff on your phone, it will take even longer. So this is how you factory resets any Samsung Galaxy phone. Hope you guys enjoyed this video and I hope it was very helpful for you guys. I'll leave this here. Please take care. Peace.


Let me just start by basically finding the use case where we're coming from. At Red Hat, we use image-based operating system. But they're not necessarily your typical AB physical block device images. They're more like virtual images. We use a system called OS tree. But really, it could be any kind of similar system. So we have a content.restore, which is the OS tree repository.  All the files in the image, or all the files in all your images if you have multiple files. And then when you want to use this, you check out a branch of this by creating a directory structure that has hard-linked backs into this content.restorage. And then if you want to use this to boot your system, in its RD, you find this directory, you bind-monet that root, and then you run from that. It's also something we want to use for container images and other kind of images. Because this entire system has certain advantages. Like it's very flexible. It's easy to do an update. You only need to add the new files you need. And do a new check out. You can use any number of images in one content.restore. It's not just A and B. You can do however many you want. So this is working very well for us, except there's one thing missing. We want some kind of tamper-proofing equivalent of what DnVerity would give you in, say, an Android system, or some kind of embedded system. And there are multiple reasons for this. The most common one is for security. You want to kind of trust the boot so you can know what you are allowed to boot. But I work in the automotive side of the company. And their safety is also very important. Like if you have an electrical interference or cosmic ray or something, it flips a bit. You don't want your matrices for your self-driving car to use data that has been modified. So there is this thing called Fsvarity. And it actually matches very well this content address storage kind of thing. You can apply it to all the files there because they're fundamentally immutable. I mean, they're stored by their content. Check some, basically. The problem is that Fsvarity doesn't go far enough. It doesn't protect file metadata. It doesn't protect directory structure or anything like that. So that's why we posted this proposal for Compose FS. I mean, it's been around for a bit. But we posted it in January, I believe. And it's kind of a combination of an image-only file system or like a read-only file system like SplashFS or EUFS and Oval AFS, where it just contains the metadata for the directory structure. But for every regular file, it just has a file name and you mount it with a loader similar to how you mount Oval AFS. And when you want to read the file, it opens the backing file in the lower directory. And if you assume that all the backing file has Fsvarity enabled on it, we can have, like, we build this image at one point and we know how all the files should be. So we can just take their expected Fsvarity digest and encode it in the image. And then we enable Fsvarity on the image itself, I mean, the file system image. And then when you mount it, you specify the expected digest. And if it matches what you got, you have some kind of, like, Merkle hash, three of Merkle hashes or something that recursively verifies all that goes into this. However, during the sometimes heated discussions about this, it turns out that there's actually features in Oval AFS that sort of makes this possible. This metadata or a metacopy attribute allows you to have a two-layer system where you split the metadata and the file contents along the different layers. And there's also a redirect thing where you can have different names of the same thing in the different layers. So the theory goes that you have a two-layer or two lower directories, and the lower most one is your content at restorage. And the upper lower, or whatever, is a loopback read-only-mounted thing that has these extended attributes pointing to the lower. And then this union of these kind of creates the same effect as an Oval AFS, or as a Compose FS. There are some parts missing here. For the tamper-proofing, we need a verity attribute. It's basically similar to the other attributes. You set this, and then Oval AFS will verify. As soon as you open a file, it will verify that, actually, yes, the file has the expected attribute. And additionally, there's a mount option that says we really require every metacopy to have a specified digest. Otherwise, we fail, because we really want to trust that everything is verified. And then there was some measurements of dubious qualities where we measured recursive lists. And it turns out that some of the performance losses is basically because of the lookup amplification that happens in Oval AFS. Every time we look up something in the upper layer, we look up it up in all the layers. It's just, in the Compose FS case, we never, ever looked up the backing file unless we wanted to try to open it. But for actually no reason, Oval AFS always does it.  If there's a redirect, we can delay looking up that until we actually need the thing. And additionally, it's like an overlay. Actually, it's a union file system, and we don't really need the content of restores to show up in the union thing. If you control the layout of the content of restores, you can use things like whiteouts and whatnot to hide it. But I mean, I added this easy way where you can do the double colon thing that separates the kind of layers. So only the uppermost ones are actually visible in the union file system, and the lower ones are only used for resolving data. And I have in the Compose FS user space code we have, code that generates images matching these plus the regular Compose FS. So you can do MK Compose FS. If you generate an EOFS file, and there's a Compose FS mount helper, you can easily mount them as used. Like it's very simple to use, from MK Compose point of the directory, it generates like a content of restores kind of thing, and you use mount the image. So I guess the basic question here in this talk is that we wanted to resolve what kind of approach do we wanna go with for upstreaming this? And this is here where the talk gets kind of short, because I think most people agree that that leaning towards using the existing code in Overlay FS is the right thing to do, because it's just one less piece of code to maintain. And also the added stuff, the digest ex-adder and stuff, it's kind of useful on its own. Like you might wanna ensure in general that your loader wasn't modified externally, even though you're not doing this kind of tamper proofing. There are some cons. I think they're pretty minor. I don't really like loopback mounts. They're global, they don't name space well. And also the upper, like lower, the EuroFS file system performs quite similar to the Compose FS1, but the combination of the overlay and the EuroFS, it does like double the amount of inodes and entries and whatnot involved. So there's some overhead here. But when we measured it, it's slower than the pure image ones, but it's similar to the recursive check out with hard links performance. So it's not really a net loss compared to that anyway, so it's probably fine. So I guess the first question here is, does this make sense? Does anyone think we should go with the custom file system? You ask a room full of kernel developers to push out another file system, the answer is almost always gonna be no. Yeah. Especially given the fact that you can accomplish the same thing with modifications to an existing file system. I understand what you're trying to get at, and there are definitely cons. Like I don't wanna say like overlay FS is like, of course, the right answer. But it appears to me that you've made our argument for us. Yeah, and I agree. I mean, I would prefer not to maintain a kernel file system. I have enough code to maintain. So, I mean, there are people here looking at fixing this problem, but if you wanna run it, for instance, in a container, you have to have a loopbook device in there. And that's like a loopable thing that can see all the loopbook devices on the entire problem, entire system. I had patches for this, namespacing loop devices properly. I sent them out, this works fine, even including, well, Dev TempFS namespacing for this. And there are Iskasi people who are bugging me with this as well, so we will hopefully have a namespace loopback devices. Not too far in the future. Just need to generalize that work. And that should hopefully be resolved. It's really just a matter of time. Seth did the initial work for this years ago. Yeah, I saw there was like, from you, I think. Yeah, I did it. Like loopback FS? Yeah, loopFS. And the reason I did it is because I could preserve backwards compatibility with, so the devices are global and the biggest problem is that they show up in Sisyphus. They have Sisyphus entries.  But namespacing it without a file system globally, the problem then becomes that I need to start hiding the loopback device Sisyphus entries in containers while currently they appear for all loopback devices on the system. So technically this would be a regression. Also in Dev TempFS. So all loopback devices also that you create on the system also show up in Dev TempFS. So my way around this was we make a tiny file system that you can mount. Each loopFS instance that you mount is a separate world. You have a control loopback device like you have in Dev loop control, whatever it is called. You can allocate new loop device nodes. They get created only in this instance and they show up in Sisyphus only in this instance. So you get this approach where you get namespace loopback devices without backwards compatibility issues. Christoph didn't like this approach. I understand like the easy solution is usually you make it as a sort of file system thingy but I think we don't need this. We can implement a way where they don't show up in the file system. Then it would be just file descriptor based which is probably a bit less nice but it would work for these use cases. You mostly could interact with them programmatically then not through the file system. I think that should be okay. So I wonder what's the benefit of actually having this accessible as a loop effesant having multiple instances of it because I need the assumption that everybody uses usernamespaces. The loopback device doesn't give you much because you can't mount anything off this. Now my talk later is supposed to address some part of this but it's at least in your use case it's not gonna help you much because you generate these the upper layers locally. So you cannot sign them ahead of time and then there's no way to authenticate that whatever you did there is actually trustworthy. I think that's quite true for my primary use case of OSTree. We do, we will be able to sign them even though we generate them locally because they are fully reproducible from the OSTree metadata. So we locally generate 100% reproducible thing and then the OSTree commit has the signatures that would you supply. But for other approaches like container images and whatnot that you just pull it from the registry that you would have problems. Yeah, actually I was actually about to mention container images. So the one of the things is that for OCI container images at the moment it's all quite, what is a nice word to use, problematic. Basically the, yeah, so one of the things is the discussion that I've had for a long time which is that we want to fix this problem. And I don't, I didn't follow, I haven't followed what the Compose of S system thing looks like but the overlay first thing described here it would match actually pretty well what we would want for container images because we would like OSTree, we would want to have the structure and everything be transparent so that you could have this, the ContraStore actually contains the files with some caveats about deduplication and stuff like that, but we'll go on now. With all of that then you would be able to have this thing which again you would be able to generate reproducibly these hashes and everything you apply them. That sounds like something we actually are actually able to use. And finally people might actually, you can actually argue that signed container images are actually useful once we would have this. But yeah, that's, yeah. So I see, at least from my perspective it seems to me like the overlay first thing is better because it seems more modular that we can actually set this up in a way that we want to set it up is my impression. Because we wouldn't want to ship like a one giant blob which internally contains all this stuff.  Yeah, that's from that side of things. Yeah, I'll bring up that this is next year. This isn't actually something that composes as the kernel plus system solves. It's just another feature that we're interested in in this space. Like we would want to generally have the same kind of solution also for non root things like rootless pod man. I just want to run random image from Docker hub and or something. Something that we don't really trust, maybe, but still we want to be able to run it as a user. And that's more complicated because like everything needs to. I don't know if you hear me. I started working on a post office for the container use case. So currently there is an extension for OCI layers like the search zip effort which basically adds some metadata to the table so you can index single files inside the table. The idea I had with ComposerFest that we would replace that JSON file with a ComposerFest blob itself. So it would be used to as a metadata for the table and at the same time, it would be mountable by ComposerFest server. Yeah, so the thing is the strategy. I know the authors are using everything. So yeah, the strategy stuff is very good and it's very good in that it fixes many issues we have with the current format. The thing is that my view is, is that the best way of solving the container image thing which is not, this talk is not about so I'll stop derailing it. But basically, I think if we redesigned it so that we didn't use tables for everything that would actually solve basically all of our problems. And I don't think that, I don't think replacing one format that you cannot easily look inside as like a random go binary that is like just understands JSON and nothing else having replacing one binary format with one that is would be better is not the best way of solving it. Like there are better ways I think we can solve this problem. Yeah, but the current approach will work with all the clients and registers that are out there. That's my opinion on the main benefit of it. Like it will work with existing clients. I think we need to add support for mounting though. Like it wouldn't be transparent. Like you would need to, like you would need work on the clients anyway. Like if it... Well, existing clients, they would use it as a normal type of a state right now. Instead of clients to learn the, yeah. Yeah, but you can't mount the GZ, that's the issue. So like you need to unpack this thing and then you're in the same problem as you always were which is that tar archives are not reproducible at all. And tar archives, when you instruct them, you don't even know what you get. And then like when you have file systems, it's like a whole, yeah, the whole nine yards. Like it's not, yeah. Well, you start GZ, tables helps a bit, right? Oh no, it is better. I'm not saying it's not better. It's definitely is better. I just think that yeah, I think that there we, there is approach we could have which would be even better than that. And I think that we could then use the overly fast stuff in the way that suits container images and you could also use it for OST or you could also use it for whatever else. Yeah, and I'll have other people request just random things. They have cast style directors for, you know, builds. They build their stuff and it generates like a commit in some kind of content of rest storage and they want to reproduce that. So there are many other similar use cases. So here are listed some possible solutions to the non-routes running. I mean, your work on Mountain 2, like having a trusted service that lets you mount these things, doesn't really work for the arbitrary image that I got from Locker Hub, but it works for like trusted image that you have signed somehow or that you trust. I tried to write the, do this using your URFS fuse. It turns out that if you enable user Xhatters with URFS, it's not compatible with matter. I'm not sure exactly why. I can sort of see that it might be a possibility to access files from the lower that you shouldn't, like if you have access to the upper there, you can create a metacopy. For some reason it's not possible and we don't quite know. I do believe that in this particular use case where we have a read-only lower, it shouldn't be a problem. Like there's no upper, writeable, there's no writeable lowers. It should be safe here, but making it possible, it might be hard. Another approach is to use Hava. Just diffuse and reimplement the redirects and everything. That will perform poorly unless we have these fused pass for work that was discussed yesterday. So it's all these kind of things tied together, I guess. Yeah, I think for me, the generic Locker Hub images are not trusted, set it on the tin, and you should not be able to mount them from inside a container, at least if it has an on-disk format. So the mounting into namespaces work that he's going to talk about is an initial implementation, how we can do this today, but we already talked about this yesterday. Ideally, we'd extend the new mount API in a way that you can register yourself as the, let's say, mount manager for that particular namespace, in a similar way like autofest works that you can say, I want to supervise the mounts of this particular, or just a subset of mounts, of this particular mount namespace. And then the mount manager, which probably in this case would be SystemDemount, MountFSD or whatever, would get a notification if a mount is requested from inside of the container. Then the outstanding things that are missing that David pointed out yesterday would be a way to inspect the requested mount options for the FS context, for example, and then to be able to allow or deny based on that. That would be an extension which would make the work that Lennard's going to present much nicer because currently it works fine. It's just more involved and this would be a more direct, elegant way of supervising on mounts. I'm not really sure if any chain of trust can protect you from delegate the trust to who? I'm not sure I follow, sorry, it's like you have a privileged process on the host like SystemD, for example, who has an idea of which images it thinks is safe or which it has safe. And obviously trust needs to be in for the foremost cases be established through de-embarity in this case or other mechanisms where you can ensure the integrity of the, how does the data help your case? So it doesn't have the random image from Docker API use case, but it does true. Like you have a set of images that you produce yourself that are signed by Microsoft basically and you want the user to be able to run them as the user not, and it works fine for that. And it could work with Composite as that way. I'm not providing, I'm not arguing that I can provide a way to let users mount random crap. I don't think that's ever going to be the case. Is there any other disadvantage of the EOFS fuse approach apart from the technical issue? Well, there's also performance I guess, but I think. So one thing like part of the stuff I present about is also that we want to provide an API for unpolluted containers and things like that to create their own file systems that they then can mount and extension of that could actually be that you just implement your own little service that runs on the host that, no, no, no, no, no, no. You would run it on the host like on the Alta level. You would run a tiny service that simply provides services for containers and you mount the socket into this. No, it does because it could pass a, like it could sign locally either with DMVarity or it could even, I don't know, DMVarity doesn't really matter if it needs to be vital. But basically what I'm saying is that you could have a local service that can generate locally signed images for you if you give them input that can be validated as somewhat reasonable, right? So that then, once it actually gets passed to the kernel, it's known in a good state. The essential problem of why we're not letting users mount file systems is that the means to verify that the image is, the binary is safe. There's no way to verify it. We need a BPF verifier for images. No, again, what I'm saying is basically like the way how the stuff that I'm gonna talk about, I'm not a per name, I'm talking about this right now, that is that we somehow have to establish trust. The obvious way is DMVarity, right? But DMVarity, like you can generate the DMVarity data locally. Let's say we had a system level demon that runs on the host is accessible to containers. However, you talk to the demon, give it the redirection information like the list of files and the hashes. It then generates a EuroFS, automatically signs with DMVarity and returns you that finalized image. Then you've suddenly have a service that generates you trusted images and you can't fake them and they will work only locally in the local concept and otherwise they won't. Sorry, my English is not good enough but I want to clarify something. For your use case, I think for most use case, I think the views and pass through, maybe pass through implementation is good enough. I want to clarify why I introduced the EuroFS because we know that read only use cases is quite limited compared with generic file systems but currently in kernel implementation, I think that we only have SquashFS. The other FS such as roomFS and crimeFS, I'm not sure how many users to use them in the last scale but what we once introduced EuroFS because that's who we want to make use of the page cache to do in place IO so that we want to reuse page cache to for compressed data first and to do time sharing. I will give this topic for following. So that is mainly about the performance of the generic compression performance. So we want to boost up the generic performance and from my own part, I want more people to join no matter what FS, maybe roomFS or crimeFS or SquashFS or EuroFS and maybe more developer could join us to improve a more powerful file system so that we can join together. So less fragmented. That is my own opinion of this whole topic because from a technical point of view, I think this FS are much similar, particularly for your use case. I think just a few, maybe enough. It's a good opportunity to thank Alexander and invite you to talk about EuroFS. It's your turn. Thank you. I'd like to say about what you said about EuroFS was I think really introduced to improve usage and energy usage but it mainly improves for the data read case and what ComposeFS really now needs from EuroFS is metadata only. No, I'm just saying that initially you introduced EuroFS to make data access, read-only data access more efficient but now that's a metadata only use case for EuroFS but it's a great opportunity because like you said, we know that overlay FS uses XSATOS in this use case a lot and XSATOS are like the bastard child of file systems. It's not optimized. Many file systems do not optimize it. It has a very large overhead, especially for this kind of use case when you need to compact small inodes, you get XSATOS can mess up your performance but this is an opportunity for another thing to optimize. You have a compact format for small XSATOS and I know it together, that's a major win for these sorts of workloads.


Okay. Hi, I'm Sophia. I am going to be the president of ASU this year. I'm very excited. This is kind of our first little event, so it's kind of nice to start seeing people again and kind of getting back into the groove of being in Burlington over the summer and all that. So yeah. How would you say your experience has been so far as an Asian student in a predominantly white campus? Um, I definitely say it's interesting. I think that freshman year was weird because I came in during COVID, so I wasn't really involved in anything like MCSC or ASU or anything. So I think it was something that was never really on my mind. It was more just like, let's just get through this year as a student who's homesick and misses her parents. Um, but I think like last sophomore year, last year, it started being something that I was like, oh wow, like this is kind of cool, like getting involved, and I came from like a bunch of people. I came from a really white town that was like, it was just made a few other POC in our school, so it was never something I gave a lot of thought to, but I have been really enjoying my time and I think really enjoyed my time with the people I've met and a lot of the clubs that I've become a part of and just the people who inspired me to become more involved. So I think that is something I'm truly grateful even if UVM is um, white dominated. I think that I've learned a lot from the experiences and people I've met and I think it's just something I want to take with me as I move on from UVM eventually and all that. Hi, I'm Olivia. I just graduated. And yeah, I started going to ASU my junior year and it's been lots of fun. Best memories. I think just like getting closer to a lot of people.  It's getting to know people. Introduce myself. Hi, I'm Leah. I graduated a year ago, so I'm old. I've been around town since graduating for a year now. Still hanging out with people, still seeing other people. Yeah, nice experience. I started going to the club when I was a freshman.  But I was involved in some other affinity stuff between that time and then I started going again my very last semester of school. I went like every time and that was a lot of fun. My best memory. I always liked the Lunar New Year events. I always had a really good time. Everyone would come out for those even if you hadn't come to the club that much or that regularly or you weren't able to.  We'd have good food and it was a highlight of my year. Hi, I'm Aiden Carpenter. I will be a junior at EVM this year. I don't know.  I was president of the Black Student Union last year. I'll be outreach coordinator this year. A few other things, yeah.


Ukrainian truck destroyed Russian Kamaz truck. The Ukrainian Defence Forces continue to engage the enemy with firepower. The destruction of the Russian Kamaz truck was successfully captured on video according to the Ministry of Internal Affairs of Ukraine. According to the Minister of Internal Affairs, Ihor Klymenko, the National Guard of Ukraine soldiers are eliminating the enemy on the eastern and southern fronts. Our resilient defenders are executing combat missions and destroying invaders around the clock, he writes. The Minister explains that the video shows one day of the combat work of the brigade's Special Purpose Combat Drone Unit of the Azov National Guard of Ukraine. Klymenko specifies that just in the past week, the following were destroyed, 21 tanks, 6 APCs, 10 IFVs, 47 combat armoured vehicles, 41 units of automotive equipment, radar-guided missile systems, SAM systems, 5 mortars, 4 ammunition depots, 3 surveillance systems, accurate and effective strikes were made against enemy artillery and hiding places, 40 howitzers and 34 bunkers were destroyed, adds the Ministry of Internal Affairs. It was also reported that Russian occupiers were increasingly refusing to participate in assault operations on the front. Colonel Oleksandr Stupun, the spokesperson for the Joint Press Centre of the Defence Forces in the Tavria direction, has reported that the losses of Russian occupiers on the Avdivka Front amount to about 300 to 400 soldiers daily. They are bringing in reserves.


The migration crisis operation framework in short MCOF frames IOM's comprehensive approach to addressing mobility dimensions of crisis. While internal and cross border displacement are a primary concern, there are many mobility dimensions of crisis such as trafficking and smuggling risks and vulnerabilities, effects on cross border trade and nomadic routes, migrant cutting crisis situations as well as risks for those unable to flee. MCOF informs the way in which IOM responds to crisis and coordinates with other actors supporting states to fulfill their responsibilities. IOM places displaced populations, migrants and affected communities at the heart of its crisis response with programming that responds to evolving needs and aspirations. Through five cross cutting issues, MCOF ensures that IOM's programming is evidence-based, equitable, accountable and responsible. The MCOF's 15 sectors of assistance reflect IOM's humanitarian, peace-building, development and migration management capacity and expertise applying a mobility lens to the humanitarian development peace nexus in at risk, fragile and crisis settings. This comprehensive response reflects IOM's principal humanitarian action while simultaneously contributing to efforts to address the driver's and long-term impacts of crisis for sustainable development. MCOF structures IOM's efforts to reduce vulnerabilities, needs and risk and build resilience and peaceful societies. This contributes to the transformative promise of the 2030 agenda to leave no one behind. Addressing the mobility dimension of crisis is a key component of good migration governance and contributor to sustainable development. To learn more about the migration crisis operation framework or MCOF, please visit the MCOF page on IOM's website.


So this is not a normal thing that happens at RubyConf, but my name is Sam, I'm the track director for the testing track, and I'm actually really pleased Justin is here today because I owe him a huge debt of gratitude. I got very, very ill immediately before RailsConf this year, and I couldn't come and make my talk. And exactly two days before the conference, I texted Justin who at the time was many, many time zones away, I was like, Justin, listen, there isn't a backup speaker who can give a testing talk in time, and Justin was like, all right, I'll do this, that seems fine. So why did you reject my talk in the first place, Sam? So I just want to say a huge thanks for Justin for being here, he's definitely having a little bit of a rough week, as I think many of us are, so could we all just give a huge round of applause to Justin and welcome him to the stage? I was touching. Come on! It was touching, it was not an ironic, pithy statement. Those are coming. All right, let's roll. This talk is called surgical refactoring. My real name is Sam Phippen. If you don't get that joke, you can call me Searls. That's what my face looks like on the internet. If you got any feedback from this talk, you can reach me, Justin, at testdouble.com. That's right, I come from a company called Test Double. The way that we work at our company is we actually are consultants that work on existing engineering teams to just get a lot of stuff done so that we can create some slack in the system so that we can pay down technical debt, make things better, refactor our goal in life is to make the world a better place and make software a little bit less broken for everybody. If your team could use some of that help, you can say hello at Test Double, and we'll set up a call to talk. So this is exciting. Like, there's a national Ruby conference in Ohio, and I live in Ohio. So welcome to Ohio, everybody. If you live in Ohio, welcome to you too. A lot of people who know me know that I live in Columbus, and so they expect that I know all about Ohio stuff, that I'm on Ohio, and I'm not. I'm actually from Michigan. That's right, I forsook the beautiful, awesome landscapes and lakes and everything that fill up the memories of my childhood for the rich cultural heritage of Ohio. This is the Circleville Pumpkin Show, and I'm actually not joking. Ohio has culture. I think where I found it best is in its comfort food. There's a lot of fun stuff, because you go to the Circleville Pumpkin Show, and yeah, there's these hilarious, silly pumpkins everywhere. But they also have deep fried buck eyes, which are chocolate peanut butter candies, and it's fantastic. Or if that's not your fancy, they have chocolate-coated, chilled pumpkin cheesecake on a stick. Very creative, very intense culture here. But it's not just desserts, like no cuisine is safe. These are one of my favorite dishes in Columbus are Ohio nachos. They are kettle chips covered in queso and sprinkled with breakfast sausage. It's very hashtag health. But it's not because we're into food, not because we got great ingredients. As far as I know, all we do is grow corn and animals that eat the corn. So it's not like it's about the ingredients. In fact, it doesn't really matter about the ingredients, because we're just going to deep fry it anyway. This is a grilled cheese sandwich. It is a grilled cheese that contains fried cream cheese jalapeno poppers on the inside of it. And then once they build the whole thing, they dunk the sandwich in pancake batter and deep fry that and serve it as a Monte Cristo sandwich. So that's one of my favorites. That's an example of American exceptionalism right there. The other thing about Ohio culture that I've learned since moving here is I feel like Ohioans are really competitive and in the context of food. You know, if there's food and a stopwatch, someone's going to find a way to turn into a contest. And that's something that I've learned to accommodate in my life. So Monday this week, I was just feeling like I just really wanted a good sandwich. So I thought I'd go to the neighborhood deli. I've got a lot of awesome restaurants. They're all in strip malls and they all have really generic names, but they're really good. So I wanted to go to my neighborhood deli, which of course is called Neighbor's Deli. And I walked in and I just noticed for the first time that they actually have a competitive challenge sandwich that they make.  And I was like, all right, well, that's interesting. But you know what, honestly, I'm better than that. Competitive eating, this is wasteful. That's, you know, I used to have weight issues. That's not something that I'm going to do. I'm just going to get a normal little hot pastrami sandwich. But then I looked at the list and I was like, well, you know, they got like, I could sample all the different types of meats. And then you might catch this up here. It's like, it's called the model lift. And I said, I know a few things about picking apart monoliths. Maybe I can do this. And so then I thought about all of you today and I didn't want to let you down by skipping this challenge. And so this is what got served to me, zooming out a little bit. Like most monoliths, with which we are familiar, it is falling over on itself and it was not exactly built to spec. There's like a pound and a half of corned beef at the bottom. So all of that for less than a grilled cheese in San Francisco. So what did I have to lose? Of course, I looked at that thing and I was like, nope, I'm out. But maybe I picked up some Ohio culture because the waitress said, all right, 20 minutes go. And I was like, yeah, I got this. So that's me and my sandwich.  So I made a huge mess. It was disgusting. It didn't even taste that good. And I was feeling sick. And I knew like if I pushed any harder, I'd still fail and just be sicker. So I was like, I'm going to quit. And there was this elderly lady who'd come in and she was just watching me and she said, she said, you can do it. In a sweet like grandmotherly tone. And I said, I really can't. And I shouldn't. And why are you rooting me on? This is unhealthy. And she didn't know. We believe in you. And she was really serious at that point. She's really as like, well, I give up. So don't. But I don't know if that is also part of Ohio culture, that there's just this sort of, you know, unjustified faith in others. Sam mentioned it was a rough week for a lot of us, I think. And the reason that I do these conferences, the reasons I'm here today is because I do believe in all of you. And I believe that you can do great things. So I just wanted to say that before we got into stuff. Anyway, this is a talk about failing to conquer monoliths. So I propose nothing. First, let's back up and talk about some context. I love Ruby. Ruby is obviously a super successful awesome language. If you think about what made Ruby successful in the early goings, everyone's really happy. People were building gems just for fun, just for the accolades and the attention you get for being associated with Ruby. And the thing about languages is that the early days of success are determined by your ability to make it easy to make new things. So because gems need to exist, you need to attract people to the ecosystem, and it needs to be easy to learn and pick up. And Ruby was awesome at that. But later success is fundamentally different. We're 20 years in now, right? And post more than 10 years of Rails. People are more critical. It's an incumbent. It's not the new shiny thing anymore. People are using it at work. It's in much more serious context. And a lot of money is riding on these systems being long-term maintainable. It's a very different mindset, isn't it? And so later success for languages, you look at something like Java, I would say it's based on whether you're able to make it easy to maintain old stuff. And I really don't feel like Ruby's ever excelled at that. And so my challenge in writing this talk was to ask myself, is there anything that we can do as a community to make it easier to maintain old Ruby code? And I thought about it. And I thought that the best way to, like, think about that, to pull that thread would be to let's refactor some legacy code, because that's the context in which I think most teams struggle. So if you're not familiar with terms, the word refactor is a verb defined by Martin Fowler as to change the design of code without changing its observable behavior. That's a great definition. I'll add to some purpose to it by saying, typically I refactor to change code in advance of the bug fix that I want to make or the feature that I want to implement so that the job of doing that is easier. It's like pre-factoring. It's getting the system in so that I can slot that new feature in later. The other term here, legacy code, isn't well defined. It has a lot of definitions. Some people just use it to mean old code, maybe code that doesn't have tests, Michael Feather's definitions, code that doesn't have tests, or code that we don't like. But today my definition is a little bit more specific. I say legacy code is code that we don't understand well enough to change with confidence. Whether you have tests or not, that's I think the one that best discriminates what it's like to maintain old stuff. Today we're going to talk about refactoring some legacy code. If you're here, you're going to talk about refactoring, you probably know refactoring is hard. I think refactoring legacy code is really hard and what makes it really hard is it's easy to accidentally break unrelated functionality because there's so many variables, so much complexity all tangled up. And as a result, most of us view legacy code refactors as a fundamentally unsafe thing to do and no fun. Additionally, they're hard to sell to people. The way I'd visualize that is a little two axes graph. Business priority on one axis and the cost and risk of implementation on the other axis. In the top right, you could say our new feature development. They're very important, but they're also expensive. And in the top left, you drop bug fixes. Also important to the business, they're relatively less expensive. Bottom left, I'd probably put testing. Certainly less important than those two. Obviously important to us, but also not so expensive that the business doesn't let us do it. And what goes in the bottom right? Well, if I had to put it anywhere, I'd put refactoring in that corner, right? It's very expensive and it's a nebulous business priority. So we don't have to sell our businesses on letting us build new features. That's probably why they're paying us a salary in the first place. And it's probably not going to be hard to sell but it's become normalized culturally now in software. And so typically we're afforded time to do it. But it's still really hard to sell people on refactoring and habitually paying down the technical debt in our projects. You think about why it's hard? I mean, it's because we can't necessarily predict how long a refactor is going to take us. From the business's perspective, we just said that the definition of refactoring is you're not changing the observable behavior.  So it takes a lot of trust. And additionally the areas that need the most refactoring tend to be all tangled up. And so as a result, when we're refactoring a particular area in our code, it's not safe for other people to be working there. We have to stop everything so that we can merge it in, otherwise we'll have all sorts of merge conflicts. And so it's very disruptive to do a lot of refactoring. And what you notice is that complexity is correlated with importance. So the more complexity in any bit of code, the more branches and conditions and everything else that you've gotten thrown in there, it didn't get there by accident, got there because it was really important to the business that it cover all those cases. And so the things that need the most refactoring are also things that we're most afraid to change. So if you think about sure, it's down there, it's relatively low priority because it's so hard to sell. What could we do to make refactoring a better sell to the business is the first thing we should think about. Like how could we raise the priority? Because in their minds refactoring feels like road construction. We're telling them they're going to get less of what they need more slowly, but money is going to continue to fly out the door at the same velocity that it normally does. And we have a few strategies for dealing with it. None of them good. First, we can try to scare them. We can say, hey, well if we don't refactor, then someday we'll need to rewrite everything. And that's far in the future. That's too nebulous. Or your maintenance costs will be higher. And that doesn't help because it's hard to quantify, right? The next thing people do is they try to absorb the cost of refactoring as part of their just, you know, every story. Like for instance in this little pie chart we probably spend some amount of time planning some amount of time doing development and some amount of time testing. Well the team could just agree like for every single card we're going to grow the pie and add habitual refactoring and we're going to do refactoring as part of every single story. And that would be fantastic except it requires extreme discipline which probably means it doesn't scale and it won't work on every team. And additionally, if the team is ever under any kind of time pressure, which is most teams, it's going to be the first practice that goes out the window. So I don't think that's going to be successful. The most common thing I see as a consultant is the strategy to take hostages. So the business is like hey, I've got feature one, two, three and four and I want them in this order. And then we say, oh Contreraser, you're not going to get feature two until we pay down this technical debt. And you're not going to get feature three until we pay down that technical debt. And I don't like this because it's adversarial, right? It blames the business for having rushed us in the first place. Additionally, did you know that software developers are like highly paid and expensive to businesses? So it erodes their trust in us if we tell them that this thing that we just built them six months ago was actually shoddy junk and we needed to go fix it. And if we get in the habit of telling them that eventually they're going to probably find new developers. So, yeah, refactoring is hard to sell. This is not a talk about figuring out how to solve that problem because I haven't yet. I think that there's a lot that we could do, but a lot of it's also cultural. So let's just give up on that for now. And let's talk about the other axis, cost and risk. Why is it so costly and risky? Well, from a developer's perspective, it's a lot of pressure to do refactoring, right? You have to keep a lot in your head. It's really scary, the scariest, darkest, dankest basement of the code base. You feel like you're under a lot of time pressure because, you know, getting any sort of allowance to spend the time on this stuff can be difficult, like we just talked about. And the tooling isn't really that great. Like most open source tooling and libraries are written by people who don't want to be thinking about the legacy mess that they have. It's about creating new stuff. That's where most of our attention goes. We don't think of it as being 80% of our job, even though it probably is. So the tools aren't that great. And so that makes refactors feel really scary. And if I'm on any kind of mission, if there's any theme to the work that I've been doing in my career, we try to find all the scariest things about dealing with the complexity of software development and somehow make them less scary so that I can be productive. If you're on board with that message, I think that you should buy my book. Working on a book for a few years, it's called The Frightened Programmer. That is a joke. I am way too afraid to write a book, so that book does not exist. Let's talk about what we can do to make refactoring a little bit less costly. Well, the first thing that we already have is the book refactoring patterns and that sort of approach, where what they do is they define a handful of operations, like extract method, pull up, push down, or split loop refactors. And these are safe operations that we can do in our code, but they're made safer when we have good tools through language introspection, like static analysis and Java. My favorite thing about using Eclipse IDE with Java is I have this right-click menu and I can do all these things, and I'm basically guaranteed that all the references can be matched, and I don't have to spend all day gripping around for the changes I want to make. I can't do that in Ruby, but even if I could, these sorts of operations aren't expressive enough for me to radically redesign stuff. I can move things around and make things a little bit better, but I don't think that refactoring patterns in and of themselves are enough of a solution. Characterization testing for refactors, making refactoring easy, was pioneered by Michael Feathers and his seminal team. It was published in 2004, but basically all it says is that treat your legacy code like it's a black box, and then put a little test harness around it, write a test just for that black box of code, and then just pass arguments to it, and then listen to what the result is back and write a test that locks that in with an assertion, and then do it again, and do it again, and pass in different arguments to try to cover every single case that you might anticipate and lock it all in with a whole bunch of assertions. There's no wrong answers. If you find a bug or a weird return value, you might make a note or file an issue, but the goal here is to just crystallize the current behavior of the code and not try to jump ahead to fixing stuff. And once you have that, that black box becomes transparent enough. You can go in, you can be as aggressive as you want, you can delete it all if you want, and you can refactor new units that you do understand, that you would be able to change with confidence, and then you backfill those with unit tests that actually want that system to work. Whereas the characterization test has no clue of that. But you know what, that's a lot of testing, that's a lot of work, that's a lot of sunk cost, especially because the next step after you've done all this is to blow away those characterization tests because they don't understand how the system is supposed to work, and if you were to keep them around, they'd be like an albatross just holding you back and increase the carrying cost of the code. But if you're a team that has a lot of legacy code, you probably don't have a lot of code coverage.  The other thing I've seen a lot of teams that try to do characterization testing, they fall into this trap of only half finishing this stuff, so you end up with a whole bunch of characterization tests that they come to rely on, but they never actually follow through with fixing anything, and just adds to the nine-hour build process of semi-quasi integrated tests. So that's, it's very helpful, and the third approach I see people use is akin to A-B testing, or maybe we've learned it from A-B testing.  GitHub has written a gem called Scientist that's like an A-B testing tool, but specifically for this kind of experimental activity. And, you know, I think it's great, some concerns I have are it doesn't answer any questions about how to do that big rewrite. You know, it's moving in a very big step, and that can be, you know, difficult to figure out everything that it would need to do, and how it would have to behave, and you need to have very sophisticated monitoring and analysis to be able to understand what is happening to those users who are using the new code path, which not a lot of us have. And finally, you have to be working in a business domain like on GitHub. It just goes down a lot, right? And that's fine. I'm not kidding. If it was a financial transaction, that wouldn't be appropriate. If it was a healthcare thing, that wouldn't be appropriate. So GitHub is safe to experiment with. And some of our domains are that way, and if yours is, then you can use that sort of approach. But it's not gonna work for everybody. All right, so if you do that as a spectrum with characterization testing on the left, A-B experiments on the right, so you can see this weird divide where working effectively with the legacy code is great in development, a little bit painful in testing, has almost no advice about what to do about staging and production. Something like scientists or the A-B testing approach doesn't tell you how to develop that news thing or test it locally. It might be really useful in a staging environment where you can experiment and see how things are working. It might be a little overwhelming in a production environment, but it answers those questions much better, obviously. And so thinking about this talk what if one tool could just give me a good development story, a good testing story, a good staging story, and a good production story, and kind of carry me through the entire life cycle of a refactor? Because I'm not scared at just any one of those stages. I'm scared of all of the stages of a big refactoring, and I want something to just carry me through the whole thing. And I was thinking, like, what would that tool look like? Does it exist? And I did a lot of research, and I did a lot of thinking, and I did a lot of procrastinating, and then nine months passed, and then I was like, oh crap, I have to give a talk on this. And I thought, like, I should give, like, a standard issue just-in-circles talk of 700 snarky slides and say, like, how I think things should be done. But then I had this cool idea that was like, hey, I could just write a Ruby gem that actually helped people. And so I did that instead of just writing a whole bunch of snarky slides. I practiced this, you might be familiar with this particular methodology called TDD. So I used TDD to build this gem that we're going to talk about for the rest of the talk. And this is one of the things for talk driven development where you submit abstracts that then commit you to massive amounts of work. And what you check out at the other end of this is a gem that we call Suture. So it's up on GitHub under testables or you can find it there. That's what GitHub looks like. You can install it with gems. That's how you install a gem. And the metaphor here is that refactors can be treated like surgeries. So the similarities are, you know, surgeries try to solve these intractable problems and make us feel better. They require careful upfront planning. They leverage these tools that are very different and used differently in different contexts. Just like we want to use this in these different environments and these different modes of thought for development. They follow very clear processes, not for an arbitrary reason, but because there's so much variation and there's so many distractions that following a clear process can help you understand, you know, and they have a plan for like long-term observation. Obviously, while you're under the needle, you've got a whole bunch of people looking at everything.  Of course, like surgeries, refactors can get pretty bloody, too. I mean, like, it's... things can get messy and it's a way to control things that get messy. So, suture, the way it works, is just kind of like nine features that we're going to talk through that are each there to help you out through this particular workflow and hold your hand. The first step, we plan out the refactor and then we cut what is called a seam in the code that is like a call site to the legacy code. Then we record all the interactions that pass through that seam, the arguments passed in, the results that are returned. We validate those recordings against the old code to make sure we can replay them back that the recordings are valid. Then we can refactor as aggressively as we like into a new implementation, which we can then verify by replaying the new implementation against the same recordings so like locally we're pretty confident. Once we get up to staging, because we've got all this stuff configured, we can actually just run through in a staging environment the same critical path of code, the old stuff and the new stuff side by side like a double entry accounting and we throw up an error and explain what just happened and we can use that same configuration then in production so if anything blows up in an unexpected way, we can fall back from the new path to the old path so that users are not interrupted by our mistaken or buggy refactors. Finally, when we're confident everything's done, suture is meant to be deleted. We pull it out and then we just point everything to the new code path and you can call the refactor complete at that point. That's the process and the first we're going to talk about how to plan. Today we're going to have two example bug fixes. First, we're going to have a silly little calculator service. Forgive it, but all of these code examples are very contrived for the purpose of clarity. This calculator service is supposed to be able to add numbers but it doesn't add negative numbers correctly. It's an example of a pure function. Pure functions are always easier to deal with because you pass in arguments, you get a return value. Here we instantiate a new calculator. We call add with a left operand and a right operand. We assign it to an IVAR. If you look at the implementation of this add method, it's defined here and then for the number of times the right thing is we add one to the left, which of course that's where our bug is, right? Because we're always adding and not subtracting. You're probably looking at this being like, that's really ugly code. Well, you know what, your legacy code is really ugly, so deal with it. Our theme is obviously right here. We're calling add there, so that's where we're going to be introducing suture later. The second bug is that we also have this tally service, which is stateful, which means we're having a side effect. It doesn't handle odd numbers correctly. We're going to call this the mutation case. If you look at this one, it spins up a new calculator, loops over some number of parameters in some collection and then for each of them calls tally on that number and then finally assigns the result to the total of those things. You can look at this awesome implementation here of like a lazily instantiates an IVAR called total counts down from the number and then if it hits the halfway point exactly, doubles that and adds that to the total. So, yeah, it doesn't work on odds, right? Because these are all integers. So that's pretty cool code. Call tally there, you call total there, and you realize it's not super clean, right? So this seems more complex. We're going to have to figure out how to cut this and we'll take a look at that in just a second because now we're going to cut these seams. Again, pure function, pattern emerge here. The pure function is always easier to deal with. TLDR, write more pure functions because they're going to come back to bite you less often. Because here what we do is we take this code that existed, we're going to replace the actual call to a call to suture. We're going to say suture.create and we name it something, we name add. We pass in the arguments now as an array of args and we tell it this is the old code path and we pass it the method and this can be anything that responds to call. It doesn't matter if it's a proc or a custom class, it doesn't matter. And initially this setup so far is a no op. I would do this and then I would run the code and just make sure I didn't break anything. It should continue to behave just like it normally would. It's just going to call through by default. In the mutation case we're going to cut that one now. So we look at this one and this is again more complex. We're going to replace the call to tally with suture.create tally passing the args of calc and n which you should say wait what because calc isn't an arg. How to design these seams when we don't have a pure function when side effects are involved. Pure functions, they're a black box, right? So if we call add with 2 and 8 we're going to get 10. And if we call it with 2 and 8 again we're going to get 10. And that makes all of this really easy because it's repeatable input and output. But mutation and side effects are hard, right? We call tally with 4, we get 4 and if we call tally again with 4 we get 8. So now you have to consider that it's not an argument per se in language terms but it effectively is one of our on calculator is part of the state that influences the behavior of the method. So what we could do is we could just logically say well calc with i var total 0 is the first parameter into this thing and now I can force repeatable inputs and outputs. This is counterintuitive and there's not very many other context as programmers that we have to think this way. But it seems to work here in this case. So we're going to treat that like an argument. We're broadening the seam and that means we can't just simply delegate to tally. We have to actually write a custom proc here and it's going to take in the calculator, the state of the calculator as well as the number. We're going to call tally with the number and then we're going to return the total which is something that the actual code doesn't do because it's a void, it just returns. But we're going to return the total here so that we can build these recordings that take in all the state that matters and then returns all the result that we care about so the recordings are actually meaningful and useful. Speaking of recordings, here's how we set up these recordings. All you have to do is add to the configuration record calls true and it'll start recording every single call that's made to that seam. You can also, almost every single one of these options can be set up with environment variables so if you're running a suture in a deployed environment you don't have to make source code changes to the configuration. And to record some calls you can just invoke it so you could open up a Rails console, you could create a controller, set some params, call show, that's going to record. Different params call show, that'll make a recording. Different params call show, that'll make a recording. There's a lot of cases in this ad cases. You could record via the browser if you wanted to and click on stuff and have that invoke the code. You could throw it up in production, record there and pull the snapshots down later if you'd like. That seems safe. I think it would work. I don't know. I haven't done it. The mutation case. Again, just add record calls. There's no extra complexity here. You know, pass in a few numbers, they're all even. Pass in some more numbers, great. You pass in one with an odd and an even.  Where does it go, where does this get saved? Well, Suture actually just on-demand instantiates a SQLite database in this path. You can set it wherever you like. And it just dumps all those recordings in there, creates the database if you've deleted it or if you don't have one handy. And it uses marshal.dump. Marshal is part of Ruby Standard Lib and it's a way to convert Ruby objects into Ruby later. At this point, you might be curious, does this work with active record objects? I had written Sutures for the last 10 days at the point that I was like, I should probably see if this works with Rails stuff since a lot of our legacy code is Rails. I took a look at the Gilded Rose Cata. If anyone's familiar with this cata, it's a really cool exercise that Jim Wyrick ported to Ruby in a repo here. It's still up on Jim's GitHub page. You don't have to read this, so this particular code takes in an item, we call update quality, and then I've configured the lambda to return the mutated item, and so that's what we're measuring there. And I made a little Rails app around it, so this is a real beautiful Rails scaffold. So I'm going to create a few items that are significant for the purpose of that refactor activity. They're listed in the list. And the update quality, that's where the critical path is. So when I click that, stuff happens, and then I check my SQLite database and I make sure they're good. I click it again, make sure a few more things get in. And then, yeah, so Rails apparently works, so sweet. So this all does work on Rails. I've actually tested it, and if you're interested in the example directory of the repo, you can play with a Rails example based on that. Now, we have to validate that these recordings can be played back in a test environment. So in the case of the pure function, we just write a test, create a calculator, and then the second API that we're going to look at, suture.verify, we'll replay all of our recordings. So we name it add so that it knows which rows from its database to go look up. It has to match the production one. And then the subject is whatever thing we want to test, whatever thing we want to make sure matches up with all those recordings. And here we pass in the calculators add method. Once we've done that, what will happen at runtime is it's going to verify every single recorded argument set with the recorded result, whether that was a return value or a raised exception.  All we had to do was record, but unlike the other characterization tests, we don't have this tremendous sunk cost fallacy wanting us to keep them forever. In fact, they don't feel like tests, they're just these rows in this rando database. So we don't feel any sort of unnecessary sense of attachment to our characterization test, which is one thing I really like. In the mutation case, very similar here, with Tali, here we create a lambda that behaves exactly like the production one. It should look exactly like it because we're expecting it to behave the same way. And one of the fun things is I'm not a big code coverage fan because I've seen it abused on a lot of teams and used as like a whipping metric for people, but this is actually a really useful case for code coverage because it can guide our recording activity. So if you look at the Gilded Rose Cata again, here's how Jim Wyrack, like in his example repo, he did the characterization test using his RSpecGiven library. If you've never seen RSpecGiven, I love it, it's really, really terse. But even though it's really terse, he had to write like 240 lines of custom testing. And I highly doubt he was in the mood after this was all done to like go and throw this test away and write more isolated tests later, right? So that's not ideal. Doing the same testing, covering all the same behavior with suture.verify, it was one test. I said suture.verifyRose, I passed in the subject of saying call, update quality, return the item. So yeah, it's actually dumping the item before it calls it and then dumping the result after it calls it. So the delta is captured in every single row. And then I have an additional option here, fail fast true when you expect all the recordings to pass so that it doesn't, you know, if something fails it doesn't waste your time by running all the other ones. And then before I called it done, I just ran a simple cover report. Took a look at it and I was like, sweet, everything's covered. And if something wasn't covered, you can click a button and, you know, cover it that way and then run the coverage again. So this is really fun that you can get legacy code, the hardest to test stuff, 100% code coverage, writing zero custom tests. Which I think is pretty neat. So at this point, finally, we can refactor, right? You came to do this refactoring talk so you're like, I'm a learn about refactoring. Well, I have a secret. You have to not tell anyone the secret. I don't know if we can like cover the camera lens or something. The secret is I'm actually really bad at refactoring. I don't know very much about refactoring. All I know about refactoring is that when I'm refactoring a scary code, I tend to hold my breath. And sometimes I'll go hours and I'll be like, man, I'm graying out. So you now know my secret to refactoring. Our friends in the community, Sandy and Katrina, they wrote this book, 99 Bottles at Oop. And it actually has refactoring as a very heavy emphasis. So if you do want to learn more about actual refactoring, I'd strongly recommend you check this book out. It's a cool book because unlike mine, it actually exists. So has that going for it? In the case of the pure function, this refactor, these are simple contrived examples. You look at this, the problem right is that it doesn't work for negative values. So we're going to create a whole new method. This is so we can call both of them as opposed to changing the existing one. And we're going to do this clever thing where we just say left plus right to add the two things. But I have some extra space there because I'm going to do something weird. Return left plus right is less than zero. I'm actually re-implementing the bug and then marking it with a fix me. Because remember, I want to retain the current behavior exactly, bugs and all. This is super counter-intuitive. But remember, this is a separation of concerns thing. Refactoring is not about jumping in and fixing it as soon as it feels comfortable to fix it. Refactoring is about reworking everything so that the fix can come in later. And we're not confident of that until we can be sure that it behaves exactly the same way that the old implementation did. And additionally, like it can be really arrogant sometimes to just rush in and fix a bug without considering that some higher order caller might be depending on that buggy functionality in ways that we don't anticipate. So I'm all about taking my time before I actually make the fix. Now at this point we got a backfill with real unit test. So a simple test of adding two numbers. This is the most fascinating slide in the whole thing. You add things and then you assert that they add successfully. And then additionally we're at test negative adding. So here we'll just skip that because it's not implemented yet but obviously we want to not get through this and forget to fix the bug. So then the mutation case, similar story, skips the odd values. We're going to just ignore all this, write a new one where we just lazily instantiate the same Ivar, we add, and then we return because we want to still return nil to capture the existing behavior. But we also want to reimplement the bug. So we're going to have a guard clause at the top just return if something's odd and fix me note. So Kent Beck has one of his most famous tweets. I don't know if he wrote this prior to Twitter like in a book or something. I don't read books. But Kent said make the change easy, warning this may be hard, and then make the easy change. So that's the mindset with which we're coming to this. Again, write some unit test so you call the tally a couple times, make sure two and four are up to six and then a skip pending test for the odd one. So we've refactored a code. Now we've got to verify that the new code paths can be played back against the original recordings. So in the pure function case, we're going to test it out, create a calculator, call suture verify add, pass in the subject calculator method new add. Remember, got to call the new one. I made this mistake like the first couple of times. It was like, sweet, it works. But I was calling the old one from the new test. And great, that works. That one just passes. Now the mutation case where everything is more complicated isn't going to. We're going to call this with tally. We're going to create that same land again, again being sure to call the new implementation. And this one fails. This one blows up. WTF. So another thing that the late great Jim Wierich imparted on me when I was writing one of my first gems, he said Justin, like, the most important thing any library can do is provide really excellent thorough error messages to its users to help tell them, like, how to respond to exceptional cases. So this is the error message that you get. Instead of scrolling through a long test, you're scrolling through essentially a customized markdown read me of what to do next whenever any of your verifications fail. And so we're going to break this down. First, you get a list of all of the failures that occurred in that run. Then if you look at any of those individual failures, it'll show you the arguments as well as the expected and actual return values or error results. And a couple little things to help you out when you're practicing, when you're running these things, you can set a flag to only run that failure and just focus on that one. And if you decide that a particular recording is erroneous and you can just regard it, then an API and suture to delete just that particular recording so that it doesn't show up again. Additionally, we have advice about how to deal with these failures. So at the bottom of all the failure lists, it'll talk a little bit about, like, hey, maybe the problem is in the comparison of the arguments and the results, like maybe those aren't matching up. So talk about how we compare these results. Suture ships with a default comparator. It's real simple. It compares these things with equals. You'd think that would normally work, but not always when you're dumping stuff out. So it has a backup plan of comparing their Marshall dump byte strings, and if those match up, then it considers them to be equivalent. For active record, it smells out if it thinks that these things are active record objects, and it'll compare the attribute's hashes less updated at and created at. And you can actually add additional columns you'd like to ignore on a per-scene basis. But if you're using the default comparator and your thing doesn't equal the other thing for whatever reason, and you get stuck, you can implement a custom comparator. So let's suppose just hypothetically that this calculator that we're working on has these other fields on it that we don't really care about for the purpose of equivalency, and that the subject actually returns the calculator, so we're comparing two calculators with each other. Well, the comparator would simply pass in. We've all written comparison logic. The recorded one, the actual one, and we would just return it as true if the totals match up, because the total is what we care about. Classes in Ruby are also a thing, so if you don't like writing a lot of anonymous lambdas, you can implement this as a class. You can actually extend from suture's default comparator, which means that when you implement call, you can just return true if the default comparator is successful, otherwise fall back on whatever your custom comparator logic is. Then to the to the seam, you just pass in an instance of whatever your comparator was. So going back to that error usage, a couple more things that show up in here is by default suture runs all of your tests in random order, because that seemed like a good thing to do. If you can't, if you have one that happens because of the random order, you get some order dependency, you can lock to a particular seed by setting that environment variable there, and if you know that you have to run the test for some reasons in an insertion order, in the order that they were recorded in, you can just set that seed to nothing, to nil. Additionally, I wanted to make the variable as possible. So you can see here, this is the comparator, the database path, fail fastest at defaults. You can actually limit how many tests it runs, and how much time you're willing, like I'll just run for five minutes, I don't want it to go for hours if you have a lot of database stuff. You can limit the number of error messages it prints out, and we talked about the seed. The goal here too, is like a lot of times we're not just refactoring, we're re-implementing something, and when you're re-implementing, you're starting at zero and then you're running to the other side, so it's something that you're using for a lot of errors. So, it's going to be coming up in a couple of weeks, I don't know. Some years ago I never got to use the Bar of Progress, so it was an excuse to throw a progress bar in here, but it's to give you a sense that you're making forward progress, you're at 92% this many are failing, this many are passing. It's a way to trick our brains into thinking refactoring is fun. So, yeah, I think it's really important for it's nil and it's passing in one so I think that's a hint so if we look here we're like oh right one is odd and it's returning before we've lazily instantiated total so we need to leave that put that up at the top run it again and now it all passes so this is actually a real bug while I was making the slides that like I got caught on so so that was that was affirming now let's talk about comparing the new and the old path because remember our mission is to make things good everywhere and so far we've only done development and testing we haven't really considered staging and production and the pure function case here all we're going to do is add another flag called call both to true and it's going to call both the new and the old code paths and in the case of the first one it just it just works in all cases but if you run it actually in staging I think you'll find like a lot of surprising inputs and outputs in most cases and what'll happen in staging because it's staging it'll just blow up with a big error message and explain exactly what just happened in the mutation case here we're going to set call both the true and like always it's going to be a pain because it's not going to exactly work it fails we get another huge error message with as much advice as I could fit into it and what it says there is you can see the total was two and we were called with two in the new code path returned true to but the old code path returned for and what's happening of course is the calc is being mutated Della so I added another option here called dupe args the idea being to protect against arg mutation that would actually dupe the calculator before calling either of the paths of course I was really proud of myself and that still didn't work because now that the calculator that actually we started with never changes and its total is always nil so you have to do another little trick of after you've made the mutation to the thing that you're caring about do a reassignment this is just a custom one-off thing that I had to do in this particular case it got messy most legacy code doesn't have really neat seems will have side effects and it'll it'll get messy so this one worked but you know I didn't feel real great about myself and you know got to remember like there's trade-offs right so like these features exist to try to make this easier if the if the feature is making the refactor harder than maybe it's not worth it just take advantage of the features that are useful to you but if you did pay that penalty to get it working in staging you'll also get this benefit in production for free because Mike the thing I care most about is making my changes safe for users the last thing I ever want to do is break stuff for users and if the new path errors I want to rescue with the old one so in the case of the pure function here I just change call both to fall back on error to true it'll rescue that new code path with if it if it raises with by calling the old one so everything's invisible to the user in the mutation case I already paid the price of making this actually work when you call both paths that means I can also just change the flag and we're golden it'll work fine obviously if you only call the old path when you really need to that's going to be faster than calling them both fewer side effects in case you're worried about like you know calling both things having effects and databases and stuff that's probably a thing you should check out before you throw this into production all the errors are logged sutra actually comes with a pretty sophisticated logging system so you can configure that you can merge it with your rail stuff you could just I just recommend you keep an eye on it so that if your new code path is failing 100 percent of the time you know and additionally you can configure particular error types because sometimes we expect our code to raise exceptions so if it raises an error that you expect you can just register that class and it won't consider it a failure it'll it'll let things keep passing through all right finally when we're done we just delete it so just like stitches we remove sutra once the wound heals in the case of the pure function now this is the fun part we just get to delete that test we get an api here to delete all of the recordings for that for that seam uh you know we get to sort of rip this stuff out here the old and the args and yada yada and we just call back to the original method looks just like it used to except it's calling our new path exclusively that feels good in the mutation case here uh blow away that test blow away all of its recordings uh it's like it's literally like ripping off a band-aid this is the most fun part you're like yeah go away all right be four lines again please yeah all right so you feel a lot better so you know what we did it like that was the whole refactor there was a whole step there was like pretty much every feature in this thing uh we should all feel really good note that we didn't actually fix the bugs uh so so i added this slide because i've given this talk three times before realizing that we didn't actually fix the bugs so it's easy to get carried away in the refactor step i guess uh so we did it ish uh suture is ready to use again it's up on github there we were very careful because like production is scary refactors are scary enough so we decided to release this initially at 1.0 we're going to respect Sember we're not going to make any breaking changes and surprise you um additionally i've been chatting with michael feathers a lot lately about uh ways to make production refactors safer and a big part of that is deleting dead code he just released a new gem this week called scythe which lets you register probes in production that will like basically like be tripwires to let you know this code is still in use this code isn't in use last time this code was called was x x or y date uh and to try to give your team the information that needs to justify deleting old code that isn't used anymore um and all this is part of an effort to make refactors less scary so that ruby can be more maintainable so that we can keep using ruby at work for many years to come uh and i i hope that even if you don't use these tools you'll think about this a little bit more about like maybe ways that you can contribute in the community because i've got a feeling everyone in this room has a lot of legacy rescue experience um additionally uh uh noll had this great idea uh sam betsey noll and i at lunch we're going to congregate in the same kind of area so you follow us all the lunch after this uh we'd love to chat with you about testing or any i'll chat with you about anything they'll they've only promised the testing uh we'll discuss some Ohio food over i don't know yesterday was really hearty so i'm sure it'll be good um there's one more thing i just like to say this is my own word of thanks my own gratitude uh testable turned five this week uh our our company there is no way our company would exist without ruby without this community i've got a lot of i think i can't see you all but i've got i know a few current and former like testable clients in the room we got a few testable agents in the room uh uh love all of you that like for this awesome journey like the if you told me five years ago we'd be like one of the most recognized ruby agencies in the world uh i don't know what i would have done i was already like the maximum level of panicked so i would have remained that level of panicked so yeah i'm sorals uh you find me on twitter i'd like to be your friend let me know what you thought about this talk uh share it with other people there's already a video of the version i gave in japan online so i'll tweet that out right away um testable like you know we're on a mission to fix what's broken about the world software you can send us an email uh join at testable if you'd like to join up uh we're always interviewing you know potential new agents who who want to work with us um and yeah i don't know if you know any teams that have any legacy ruby but we love working on this stuff we love the complex problems helping teams manage complexity so if you know anyone any teams that could use some help uh shoot us a message or grab me um i've got stickers and business cars and i'd love to meet you uh oh and that's the end of my slides so thank you


Let's solve a couple of questions from phase angles in series LCR circuits. For the first one we have RMS voltages across the elements in a series LCR circuit. So the RMS voltage across inductor is 70, across capacitor is 215 and across resistor is 160. The question is to figure out whether the current is leading or lagging the supply voltage, vs is the supply voltage and also to figure out with what angle with what phase angle is it leading or lagging. Alright before I get into this may pause the video and see if you can attempt this on your own. Alright hopefully you have given it a try. So how do we start thinking about this question? Now it is asking me whether the current is leading or lagging the supply voltage but nowhere in the question do I know what the supply voltage is. So maybe that's one thing that I need to figure out and when I know what the supply voltage is I should be able to tell whether the current is leading or lagging it. Now the question is how do we figure out the supply voltage? We know that this is a series LCR circuit so the supply voltage would be the resultant of voltages across the inductor capacitor and the resistor but for LCR circuits using a second rule becomes very difficult it becomes very complicated mathematically. So the approach that we adopt is drawing a phasor diagram. So maybe let's draw that containing the net supply voltage and the current in the circuit so that we can look at the angle between these phasors and then we will be able to tell which of them leads and by how much. So for phasor diagrams we we bringing the coordinate axis and we start off by drawing the current vector because then we will have a difference for all the other voltages. So when we draw the current vector then we can we can take any direction let's say the current is along the x-axis it could be any direction. Now using a knowledge of whether the voltage leads or lags the current we can try and draw vectors for all the other voltages. We know that voltage across the resistor is in phase with the current. So the resistor vector that will be along the current direction because it's in phase and you see this is v0r because the vector lens over here they represent the peak value of these voltages. So v0 it represents the peak value v0r represents the peak value across the resistor and voltage across the inductor that leads the current by 90 degrees. So if it leads the current it will be it will be in a vertical direction and it will be pointing upwards because that's how you show that's how you show that the voltage is ahead of the current by 90 degrees. Similarly for capacitor for capacitor the voltage lags behind the current by 90 degrees. So for that this is how this is how it can look like. You can see that the length of the peak voltage across the capacitor is slightly more than v0l because I'm taking some hints from the numbers that are given to me in the question. If the vc rms that's more than vlrms then the peak value of voltage across the capacitor should be more than the peak value of voltage across the inductor. Alright now let's try to figure out the supply the resultant supply voltage. So we know that v0l and v0c they are in opposite directions. So in order to find the resultant of those two vectors we can write v0c minus v0l and the resultant vector is pointing downwards and we already have one vector that is pointing to the right that is the voltage across the resistor. Now we have a right angle triangle on our hands and the resultant would be in this direction right here. We can find the magnitude even of this resultant supply voltage using the Pythagoras theorem but for this one maybe we don't really need to do that because we only need to tell whether the current is leading or lagging the voltage and we need to figure out the phase angle. So in order to tell whether the current is leading or lagging we should bring in the current vector also right. So here it is this is the current vector and now we see that the resultant the supply the supply voltage and the current they are of course not in phase with each other. There is a difference of angle and just from the look of it we can see that the current is leading the voltage the supply voltage by the phase angle of phi it is leading the supply voltage by phi. Now let's try and draw a triangle to figure out this angle of phi. So we have one voltage across the resistor the peak voltage across the resistor is v0r the length is slightly more in this I'm drawing it in a slightly more exaggerated manner and then we have v0c minus v0l. Okay now the resultant the resultant is in this direction right here this is the resultant supply voltage and the angle that we are interested in the angle that we are interested in that is this angle this angle that is phi. Now how do we figure out this angle why don't you pause the video at this point and maybe maybe try to establish a relation of phi with the peak voltages that you see in the triangle. All right one thing that we can do is we can translate this vector right over here and now this right angle triangle we can write we can write that tan phi tan phi this is equal to perpendicular upon base and in this case perpendicular is the red vector v0c minus v0l. So let us write that this is v0c minus v0m divided by v0r that is the base v0r. All right now one thing that we can assume is that this is a sinusoidal AC source so we know how the peak voltage and the rms voltage will be related to each other and those two they are related by this expression v0 is equal to under root 2 v rms. So for each peak voltage we can substitute the rms value so when we do that this becomes equal to under root 2 vc rms minus under root 2 vl rms divided by under root 2 vr rms. Okay all the under root 2 gets cancelled right away and we know the values of all the rms voltages across all the components so when we substitute that this becomes this becomes tan phi equal to 215 minus 70 divided by 160 this comes out to be equal to 0.91. Now phi, phi would be tan inverse of 0.91 and this is equal to 42 degrees. Okay so the current, current is leading the voltage and it is leading with a phase angle of 42 degrees. Let's move on to our second example now. Now for the second one we have reactants of an LCR circuit xc the capacitive reactants minus the inductive reactants xl that is equal to 40 ohms and the net impedance z is 60 ohms. The question is to figure out whether current is leading or lagging the supply voltage and again with what phase angle. The question is asking the same thing as the previous one but the information given is different. Now we know the difference between the capacitive and the inductive reactants and also the net impedance. All right again before I get into this first try this one on your own. All right now these questions which ask for a phase difference can be solved using a shortcut as well and that shortcut is called an impedance triangle. We will see what an impedance triangle looks like and also we will arrive at one going through the underlying physics and maths of it. So in this question we see that xc minus xl is a positive number. So we see that xc minus xl this is greater than zero that means that xc is greater than xl the capacitive reactants is greater than the inductive reactants and now if you multiply both of the sides by let's say the peak value of the current if you multiply both the sides by i0 if we do this then i0c this is nothing but the voltage the peak voltage across the capacitor. So this is v0c which is greater than which is greater than v0l. i0xl is the peak value of voltage across the inductor. So we see that the voltage across the capacitor is more than the voltage across the inductor just for this case for this particular series helcia circuit. That means if we have to draw a phasor diagram to find the net supply voltage the diagram would look somewhat like this. It is similar to the one that we got in the previous question where the voltage across the capacitor was more than the voltage across the inductor. We can also draw we can also draw a vector for the current and that will be that will be in the same direction as the voltage as the voltage across c resistor because voltage across the resistor is in phase with current. This right here is let's say this is i0. Now over here in place of v0c in place of v0c we can write just like this we can write i0xc and in place of v0l we can write i0xl. Also in place of v0r in place of v0r we can write i0r. When we do that then the phase difference the phase angle tan phi or like just five when we do that when we write tan phi this would become this would become equal to v0c-v0l divided by v0r but in place of v0c we are writing i0xc and in place of v0l we are writing i0xl. Similarly in place of v0r we are writing i0r. One thing that would just get cancelled right away you can see is i0. So let's see that this is i0xc-i0xl divided by i0r. So i0 just gets cancelled and now what you get is tan phi which is a phase angle this is equal to xc-xl divided by r. So this means if if there's a relation like this then this means a triangle there are triangle can look can look like this over here. In this case a phase angle this is your phi and tan phi is equal to xc-xl divided by r. In place of v0s in place of supply voltage you see z because v0s this is equal to this is equal to i0 into the total opposition that is provided by the circuit and the total opposition is the net impedance so this is i0z. This right here is called this right here is called an impedance triangle. Now for such questions where we know the net impedance or let's say the resistance and also the capacitive and inductive reactances we can directly use this triangle to figure out the phase angle between the current and the supply voltage. In this question we had xc greater than xl but if the inductive reactance that is xl if that is greater than the capacitive reactance then the triangle could look like this and the phase angle would be this angle right here phi. Now you can use these triangles right away to figure out the phase angle that's a shortcut but it is important for you to know the underlying physics and maths behind it so that even if you forget this triangle you can still arrive at it using the maths in the physics of it and we did that all in the previous question in the previous example and also you can see how we arrived at an impedance triangle using the voltage phasor diagram and finally arriving at a triangle which has reactances and resistance and also the net impedance. All right now using this triangle why don't you pause the video use this triangle and try to figure out the phase angle what is the phase angle between the current and the voltage. All right hopefully we have given this a try so in this case now we do not know what the resistance is we do not really know what the resistance is so we will try to avoid the base of this right angle triangle and in doing so we should write we should write sine phi this is equal to perpendicular that is xc minus xl divided by z the net impedance in this circuit and xc minus xl is 40 this is divided by 60 so this becomes this becomes 2 by 3 and phi would be sine inverse of 2 by 3 so this would be finally 42 degrees this would be 42 degrees and also we can see that the current in this case is again leading it's leading the supply voltage as you can see from this diagram the current is leading the supply voltage and this phase angle is 42 degrees so whenever the capacitive reactance is more than the inductive reactance in a circuit current will always lead the voltage and when the inductive reactance if this is more if the inductive reactance is more than the capacitive reactance if this is a positive number then you can see that the current in this case would lag the current would be along along the resistance it would lag the net supply voltage all right you can try more questions from this exercise in this lesson and if you're watching on youtube the exercise link is added in the description