 Hello, I'm Yuriy Stranski, welcome to my talk about efficient software development. Why talk about this? I work on OS Migrate Project, which is an open-stack cloud-to-cloud migration toolkit, and we have a very small team of three people for everything around the project, from development to testing to documentation writing to CI operations, and we do have quite a complex CI, and to supporting users who run into any issues. So in general, there's quite a lot on our plate, you can probably relate to that. So how do we deal with that? Do we work insane hours? No, that's not good for health. So we need to improve how we spend the time at work. One way to do that is to try to increase the intentionality and decrease the aimlessness of what we're doing. So asking questions like, what am I solving by doing this? Is it worth the time, or how much time is it worth? For existing activities, that means retrospecting and not getting stuck in obsolete activities. And often it's the activities or patterns that everyone in the team dislikes that need some sort of looking at. For new goals that we want to set for ourselves, it's not enough to ask, what do we want to achieve? We need to at the same time ask, how much of our time is this call worth? If we solve it, how much benefit is it going to bring? So how much time should we spend on it? And then we may find that we need to adjust the scope of the goal, or maybe drop the goal completely. Experiments. Do experiments mean going aimlessly? No, then you don't have to mean going aimlessly. Experiments are often necessary for progress, but we have to still define why for the experiment, the scope where we want to go and where we don't want to go and time box the experiment. We shouldn't get just carte blanche to do whatever we want for however long we want. The other way that helps quite a lot is to increase order and consistency and decrease chaos in whatever you're doing. There's some sort of inherent complexity to the tasks that you're doing, you cannot remove that, but there is often also some edit complexity which comes from doing things up suboptimally. So this is from inconsistencies doing chores manually that should have been automated and just generally not being organized enough. There's a concept called decision fatigue. Basically it means you have some sort of brain power for the day. And if you deeply that the quality of your decisions goes down and you start avoiding decision making. So we should keep our brain power for the important things. So everything that can be automated should be automated. And if we cannot automate something, then we should at least document it so that we don't have to hold it in our memory. The other point here is about consistency and it's that consistency with existing suboptimal patterns can be better than doing an isolated improvement. So if we're making a code patch, first and foremost, we keep it consistent with the rest of the project. And then if we want to improve something, we make sure to improve it everywhere and not in an isolated place so that we don't bring chaos into the project and we keep consistency. Some examples of sort of this period of efficiency that we do in OS Migrate, that's our CI philosophy. And CI is the focal point of QA for us. We've automated all testing that we possibly can. And there's a strip separation between the tests and CI. The interface is documented very well. And what that gives us is that all test types are runnable by developers exactly as in CI. If a developer wanted, they could set up their own CI based on the documentation of their interfaces. We also have a containerized developer and CI environment. So we containerize the common dependencies so that we have more commonality and consistency easily achievable in the project. Our testing philosophy is we strictly separate internal logic in the code from external API calls. And that allows us to write unit tests without mocks. Mocks tend to be a maintenance nightmare because they get out of sync with the real world and then they bring more problems than they're worth. So with that, we write unit tests for pure internal logic only. And whenever we want to test something involving external calls, we write functional tests for that. And this combination gives us tests that are easy to write and maintain. For CI operations, we have the complex CI of three OpenStack Clouds, including five routers, 11 subnets and 20 servers. But that's all virtual. So we don't have to deal with hardware procurement and hardware obsolescence. And it scales dynamically. If we wanted to have four clouds, we can have them tomorrow. It's all software defined and automated. So if something goes wrong, we can rerun the automation and the CI can get redeployed. In summary, the general points. Keep costs and effort proportional to benefits. Define your OIs, your scope, time box, do retrospectives. And keep your brain power for the important stuff. Find everything that you can, document the rest, and keep consistency in your project. Thanks for listening and have a good day.