 Hello everyone. I'm Tim. I've come here this week from Australia where I work for a company called IceLab and I'm also a core developer in the Dryer Bee and Roma Bee Ruby projects and it's really great to be back in Singapore. I really love this city. It's great weather, great food and in fact so does my whole family. This is us here. We're here for last year's conference. We had a great time. Our daughter was one and my wife was pregnant and here we are now, back again. My first daughter is two years old, second one is seven months and I was thinking about this and I'm actually scared about what might happen if I come back next year. We have space for any more kids in the family. On to the technical things. I'm sorry to say I've got nothing to add to Matt's comments about EVMs or JIT Compilers except to say that I'm glad he's taken care of it. I'm here today to talk about something a bit different, building applications with Ruby and I want to talk about a big shift in the way I've been writing Ruby apps. I want to talk about functional architecture and I want to start our journey to functional architecture strangely enough with object-oriented design. That's that thing we should all aspire to as Rubyists, right? We have these object-oriented design principles like SOLID to guide us and we know these things are important and that they should influence the way we write our code. We've been hearing this for years and myself I'd watch talks about these things too and I'd say to myself yeah this is how I should write my code and then I'd go back to whatever app I was working on and quite frankly it was a mess by comparison and I think my problem was that in my mind I tend to opt for pragmatism over design and I'd reassure myself and I'd say this situation is okay I'm a practical developer I've got work to do not architecture but not surprisingly the longer I worked in these apps the harder and harder they became to change and that's really the point of architecture and design as applied to code these things aim to make change easier and a well-designed code base it should stand the test of time and we can see these things illustrated through Martin Fowler's design stamina hypothesis. We can lay it out on a chart on the bottom axis we have time on the left we have features built and then if we start an app without paying much attention to design we may get a really nice initial spike in features but over time that curve kind of levels off as it becomes harder and harder to build new things but on the other hand if we pay a bit more attention up front to some good design approaches well there might be a bit of a slower ramp up but over time we get this really steady ability to add new features so we see here we had this line between them this design payoff line and I think my problem was I spent too much time thinking about the bottom left of that chart when really I should have been thinking about all the space above that line there and I think this is a mindset that's perhaps endemic in our community in general because we found our fame in enabling those early-stage productivity wins my problem was also that I wasn't comprehensively applying better design to my applications some band-aid approaches here and there they might help but they're really not just enough and since our job as developers is to deal with change and given the truth that we can't always predict where change will come from what we want ideally is a good design that covers our apps in their entirety so that's why I'm here today so everyone stand back I've got something to say functional programming will save us well maybe not quite like that this is a Ruby conference after all and I know many of us toy with different languages but I can still consider myself happy with Ruby and a practical Rubyist and likely Ruby itself is a practical malleable language and it's going to allow us to model things nicely along functional lines so the first step towards a functional architecture is a change in mindset it's about viewing your app as a series of transformations we get our initial data a HTTP request for example and then we can run that through a number of transformative steps with each one passing some data to the next until at the other end we get some HTML so in another light an app modeled as a series of transformations is really an app modeled as a series of functions and this is where we get to the functional part of functional architecture we can model these functions nicely in Ruby as functional objects let's take a look at one now so for example today we're going to be making an import products class so we're building some kind of online store we'd use something like this to populate our end product database from some third-party feed of products and even in this code right here we see the first quality of a functional object usually we can name them by a verb and then it has a call method just like Ruby's own language level procs occasionally we might want a few more methods but most of the time call is enough that call method accepts an input goes to work on it and returns some output now during the lifetime of that method it shouldn't mutate the state of the object it shouldn't mutate the input data so this is one of our big departures from a classical object-oriented design approach our functional objects separate data from behavior they don't keep that data anywhere in their own state before we go any further let's actually take a quick look at what this method will actually need to do the first thing it will do is get a list of product records by downloading that feed and then it will work with a products repository and will loop through each of the products and create a record for each one in our own database as a pretty clear and simple method to read and that's because it delegates a lot of its work to some dependencies these are what allow us to break down our application into smaller more usable objects here are those dependencies again we have download feed and we have a products repository and from here we can see the next trait of a functional object those dependencies are passed in by the initialized method and then we capture them in the object state and once these are set they never need to change and what you're seeing here really is constructed a penalty injection it's one of those classical object-oriented design techniques and it works great with these functional objects so the end result is that we can initialize this object just once and call it many times over and once it's constructed it can then go on and be passed around to become the dependency of other objects too now you might be thinking what we've seen so far is really just one of those service objects that we occasionally sprinkle around our apps where we see hotspots of complexity and you'd be right this is one of those things but what I'm proposing is that we use this approach to build out the entirety of our application's functionality because we want that comprehensive design approach so all of that crud that we used to building well they can be functional objects too so that's how we take care of the behavior side of things what about data how do we handle that we can do that courtesy of types and this is Ruby of course so they're not going to be the capital T types that we might expect in other functional programming languages instead what I'm really talking about is modeling these as value objects what is a value object it's just something that holds a particular structure of data that's unique to our business domain and then once we have these objects created we should treat them as immutable once they're initialized their state should never change too and of course we can add behavior to these objects in classical object-oriented style with extra methods working with the values we already have and the nice thing about these being simple value objects is that we can then easily pass them around all of our system and not have to worry about unexpected mutations or any sort of behavioral side effects along the way and in this way the value objects become first-class objects within our system all of our functional objects can depend upon and work with these structures so in a Ruby app built with this style objects tend to break down into these two different things values and functions our values hold data and those functions operate on the data our values are inert they're passive but the functions they're active they go to work on those values and if the values are the content in our system the functions form the pipeline through which the content flows in order for us to achieve some kind of outcome so that's the breakdown values work out to be pretty typical object-oriented code but let's take a close look at those functions if we built an app around them how does that design approach measure up well I think first thing we should acknowledge that with Ruby been grounded in the object-oriented world and with us using objects to model our functions we should acknowledge that what we're doing here is really more a blend of functional programming and object-oriented programming so with this in mind let's see how we measure up to some of those long-standing object-oriented design principles and I did mention solid before so let's see how that plays out here here we have those five familiar kind of dense little principles they're coin they're from Robert Martin and they're all concerning class design and together they can actually work as a decent indicator of what is well-designed object-oriented code so we'll step through them now we start with a single responsibility principle and this one I'm sure we're all familiar with it says that a class should only have one reason to change well how about our example here it's functional so it's named after a single verb it's got a single responsibility running the import for our products so there's no room really for anything else there if you want to add other behavior that goes somewhere else so I think we're off to a good start we'll give ourselves a tip the next one the open closed principle says that entities should be open for extension but closed for modification or in other words when we're building a system and we want to introduce new behavior what we should do is rather than modifying the old objects we should create new objects that work with the ones we already have so this plays nicely with our single purpose functions let's say for example that we wanted to send an email notification at the end of our import well given that our existing import products function is closed for modification we can extend its behavior by writing a new send import notification objects and then wrapping them both in a higher level coordinating object that runs the import first and then sends a notification and the beautiful thing about this approach is that the more of our code we leave untouched the more confident we can be that our system will continue to run bug-free even as we go and introduce new changes so that's another tick and now we come to the list of substitution principle and this one is really specifically about subclassing and ensuring that a subclasses interface doesn't break the compatibility with its parent interface and since we mostly rely on composition we don't subclass much at all I'm just going to chalk this one up as an easy win I'll set those those baselines for myself here so come now to the interface segregation principle and this one says that many client specific interfaces are better than one general purpose interface or in other words when you depend on an object and it has interfaces for many different users you wind up being affected by changes to that object forced upon it by those other users well in practice let's see how this plays out we have our import products function we've been working with and it has that download feed dependency where it gets the product information from a remote feed source and this object at the bottom here has effectively been working as an incoming product source but what if we wanted to go and add a different way of getting product information say by allowing users to upload a feed file directly well since that is also about incoming products should we go and add that extra behavior right there onto our existing objects or we could but that would violate this principle because we'd be turning the object into a bigger general purpose interface and that means it's likely going to change for different reasons over time and upset the stability of our app so instead because we've designed our objects to work really easily with different dependencies it's much easier and much more natural to model this as a separate object with a new client specific interface purely for the purpose of handling those uploads and this means that other objects can work with it as required so nothing should become unexpectedly unsettled by changes to either of these dependencies so four ticks down one to go we've got the dependency inversion principle and this one states that one should depend upon abstractions and not concretions or in other words we should care more about interface rather than implementation and this is another place where our functional objects really shine because they're oriented around dependency injection all those things do come in as abstract objects just like this our import products function doesn't know the concrete implementation details of that downloader just its interface just that it responds to call and that it accepts a feed object this is really Ruby's duck typing in a nutshell so this gives us the flexibility to adjust the implementation details of this object whenever we need or even replace it entirely and as long as we retain that interface everything stays good so how did we do well it's looking like we're solid and what this shows is that by following a functional approach we've actually created better object oriented code for ourselves and we've done this with a blindingly simple approach really to our design model our behavior as functions I mean we satisfy those principles so easily you just have to think about why they were there in the first place and I think it's because it's too easy in classic object oriented design to get off to what feels like a good start and then go down the wrong path and wind up with something that's vague unspecified hard to reason about or just really complicated so we need these principles to try and keep us on track whereas with our functions they're dead simple and they take care of so many of those design considerations for us and I think this approach is confirmed by how you would test these as well let's take a quick look at that we start by recalling those two dependencies we're using download feed and the products repository and then we set them up as mocks or test doubles then we can create our object under test the import products object by passing those doubles to the constructor then create a real feed value for our input and then return fixture data from a local file whenever our fake download object is called with the feed and then finally we get to our test so we can call our importer and assert that our repository has been asked to create a record for each of the products in that fixture file and that's it we could use these mocks so easily because our class was designed around working with injected dependencies so instead of having to interact with these two different external systems this remote HTTP data source and our local database instead we could simulate their behavior focus on our one function under tests and get the test done quickly in real isolation and with a minimum of fuss I think if there's been a strong theme so far it's been about making our dependencies clear and I think this is because it's one of the best things that we can do to enable well-designed code. Making our dependencies clear means that we can consider the stable dependencies principle as we go about and build our apps and this one says that a stable object and objects dependencies should be more stable than it is. What does this actually mean and what does stable mean here? A stable object roughly means hard to change and an unstable one is one that's easier to change and every application will need its share of unstable objects because applications do need to change but we want to avoid the situation where the objects that we need to change often are the ones that have many users within our system. So what we can do then is take our dependencies and arrange them according to this principle and make sure the lines flow in the right direction from unstable to stable and we can take this a step further. Once we start thinking about our dependencies in this way we can extend this thinking to our app as a whole and consider it as a graph of objects with the classes or objects as the nodes and the dependency relationships as the edges and historically the way we've built our Ruby apps has been really class focused that we consider those classes the nodes to be the primary determinants of what is a well-designed app but I suggest we open things up a bit and take a hard look at the edges in our graph, those dependency relationships. If we find the edges being arranged like this so our application looks like a big directed a cyclic graph this is a good sign that this app is going to be easier to change whereas on the other hand if our graph had edges flowing in the wrong direction or becoming tangled or even becoming cyclic then it's a sign we could be in trouble. That's an app that's going to be more resistant to change and that's really why we have all these principles in the first place. They're there to help us achieve apps that are easier to change and as well it seems like a functional approach to design gives us this and it does so in a way that's easy to follow based on that widespread use of functional objects and clear composition of dependencies and that's a pretty neat trick I'd say but I don't think it's a trick not a party trick because it doesn't fall down under pressure. This is an approach we like so much at iSlab where I work that is now our preferred way to build Ruby apps and over the last two years we've built more than 10 apps like this across different domains and with different levels of complexity and I can tell you this it works really well and from this simple reorientation around functions we started to see a new high level architecture start to emerge because when we have many small single responsibility objects well at that point we need to take time to devise a sensible and consistent approach to naming them and organising them and that leads to an emergent modularization of code within our system and as we see those modules emerge we can start to take notice of their boundaries and from this we can start to see distinct subsystems become more visible in our apps. How does this actually play out? Well we start with that graph of individual objects like we've been looking at and after a while we note some of them are clustered together because they all work towards a shared high level goal. So we can formalize that and turn it into a distinct subsystem and as we go other subsystems start to emerge as we develop the features of our app. These are the things that make up our applications core behaviors and because we've started paying attention to boundaries we can also take note of the application's own outer boundaries as well. So rather than being deeply tied into the app as a whole systems like the HTTP interface, the persistence layer and integrations with external services well they can be all carved off and placed at the outer edges of our app and formalized with a clear public API and clear and obvious points of integration and this arrangement this is what makes changes to our app just as easy at the very high level as they should be down at the individual object by object level. If all of this sounds attractive to you and it's something you want to give a try well you don't have to worry about going out on your own and forging your own path to this because we've seen a whole Ruby ecosystem that's grown up around this approach. We have ROMRB a persistence toolkit designed from the ground up to work as a separate layer from your business logic and then we have the project which is a collection of gems built to enable specifically this design I've been looking at today. Dryaby gives us things like type definitions, data validation, view rendering, all modeled as functional objects. It gives us the ability to consistently model both a success and failure results of our functions which lets us compose them in interesting ways and on top of all of this we get nice pattern matching support so we can elevate failure handling to be a first class concept within our system and we also get support for building our own value objects with strict data attribute types so it can be confident about what they'll hold. Now all of those things are worthy of their own talks but I just want to draw attention to one gem here, dry system. This is built specifically to help us create apps centered around functional objects and what it does is offer us a central container through which we can access all of those objects in our system. That container populates itself based on some simple conventions, it starts by scanning our source files and then for each file it registers a matching container identifier and then when we resolve that identifier we get a pre-initialized instance of that object ready for us to use with all of those dependencies automatically provided. So if you're looking at our examples before and wondering well how do I get everything into the object this is something that will let you do it easily and it lets us do this by giving us this simple mixing that we can use in our class files where we specify our dependencies by their container identifiers so instead of having to manually build that constructor and set the instance variables like we just did before we can now just declare our dependencies upfront and the rest is done for us and with all that in place we can now make an instance of our object directly without supplying any arguments at all to that initializer and again when we do this all of those default dependencies get provided from the container. Now if you think about testing this opens up some new possibilities for us because we can now choose which of the dependencies we want to leave as the default and which we want to replace with something like a test double. So instead of that arrangement like we had before where we had to provide a double for every single one of the dependencies what we can do now is we could choose not to provide one of them just like here we can choose to skip passing our products repository as a test double so the resulting objects here will use just one double and then it will use the real system objects for the repository so if we went to test this object it would actually write all the records to our test database and then we could go and assert for their presence afterwards. Now this is a much less pure approach to testing but for certain situations it may be much more practical and it's great that we have the choice to do one or the other. So that was it pretty much a functional architecture for Ruby apps. How did we do it? Well we really created a hybrid of functional and object oriented programming based on some really simple rules we separate data and behavior and then we provide that behavior as functional objects but we also draw on the best bits of object oriented programming like dependency injection and object composition and by doing this we found an approach that's easy to follow and makes for apps that are easy to change and I think this makes a functional architecture a real practical choice for any Rubyist out there and thanks to organizations like ROMRB and DryRB there's already support in our community for following this approach and also by choosing to stay with Ruby and take an approach like this rather than trying to follow it with some other programming languages you get to continue to leverage all of your existing knowledge of our community our ecosystem and our tools. So given all of that I think functional architecture is really worth a try. I think it can help you build better things because while solid might be an acronym for a collection of design principles I think it's a feeling too and I know that since I've been working in this way I've been shipping apps that feel a hundred percent solid. Apps that I'm confident will work the way they should and apps that I know I can go back and easily change later. Now I feel that taking this journey has made me a better developer but most importantly it's made me a happier developer and I'd love for you to join me and if you would like to join me now's a good time to check out this repository because we're just getting started in building a best practice example app that follows all these principles so it's fresh as of this week if you follow along you get to see all the work that we do to make it happen and with that thanks very much for listening and I hope you give this approach a chance. Thank you very much. All right so do we have any questions for Mr Tim Reilly? Anyone? Yeah. Hello first thank you for the talk it is really good so my question is about how do you develop these kind of components I mean for example did you first draw some kind of diagrams before starting to create the objects like import products download fees and the repositories and you know in which order do you go from bottom up like repositories first or you start top down and putting in testables first things things like that I would like to know about the process which you used to design and develop thank you. Thanks for the question I guess a top down or bottom approach bottom up approach is there for you to take depending I guess on your personal style and how much you know about the requirements up front so if you were sort of doing exploratory development or your requirements for fuzzy definitely you'd start with a top down approach you'd make your highest level thing and then you start to explore what it has to do and as you notice starting that object starting to do too much that's when you break it apart and build extra objects as dependencies and because we work with that auto-inject mixing as the way we work that's a really low friction thing to do there's not much effort to make a new file and then Chuck can identify for that file and there's a dependency so that's that's one way to take it or on the other hand if you really knew exactly what you wanted to build you could start with yeah the solid underpinnings and then go upwards so either options there it just depends on yeah the situation of the app that you're developing I think. Can I have one more question? Thank you. So when going this way do you have the feeling that when you want to implement some features you have to change you have to make change to the several files instead of one file for for example if I want to like add something to import products like it needs a new behavior like aggregating data from the repository but the repository doesn't have that functionality yet and in the end adding one thing requires changing in multiple files yeah do you have that feeling? Yeah this is something that definitely happens but I think it's an advantage because think about the opposite if you had all of your behavior clustered into one class and that means that much more of your system is all using that one class and then it means that any change you make there is going to have ripple effects ones that you may not even expect whereas with smaller responsibilities broken apart into a larger number of individual files you can see much more clearly because of the dependency relationships what is using what and you know what each one should be doing specifically because it has that single responsibility and in some some objects that do have sort of wider APIs like repositories where you might want to add lots of methods for different ways to access your data because those methods are clearly named by you the developer you've explicitly made an API you should be much more confident about where that's being used and you can make that change it is just a change in mindset that whenever you work on things you jump across a few different files rather than the smaller number of larger bulkier files Is there anyone else? No? At the top there Oh yeah Oh okay could you approach from the microphone sorry Okay hello So my question is if I want to migrate an existing application up and running production application so what would be the complexity to move it from OOP to functional programming approach? That is a tricky question I guess dealing with any legacy application comes with a whole bunch of nuance but I think this is something you can do as long as you're willing to accept that you're going to live in this world where there are sort of two different approaches to the design and I guess commit to the new approach whenever you add new functionality I mean you can certainly use dry system within Rails for instance there's a rail tie for it what it would mean though within a Rails app for example is that you're just going against the grain a bit so that may cause you a bit of friction but on the other hand these are all making a web app with dry web and any other web front end these things are all rack compatible so you can put them together in various ways you could even choose to carve out the new stuff and have it in a sort of separate rack-mountable app alongside but that's quite an extreme approach so I think a more sensitive approach would probably be just to start thinking about as you build new functionality think about how it can be modeled as verbs and put them put those things as classes in your Rails application wherever it fits and start working with those and you can hide for instance active record queries behind query objects you could also hide operations that write to the database behind their own objects and then your active record models for instance then become a much lower level persistence-related detail rather than something that you use as the primary API of your app so that kind of mindset you can apply to any tools that you're using whether or not you use these gems or something else so that's how I think you do it okay thanks cool is there do we have more questions yeah thanks for the talk Tim do you think that Ruby would benefit from adopting some of the patterns you were showing for example value objects could be part of the Ruby core basically and people would probably pick up this pattern way easier you know this thinking of like okay I have an immutable read-only value object if it was in Ruby core directly what do you think should that kind of stuff be in Ruby or not because that's what my talk is about I'm happy to wait and find out this afternoon no I think Matt's point about compatibility is a really good one there we can do these things in Ruby by convention an object can be immutable within our system if we choose not to mutate it and if we choose not to create writers sure I think some facilities around first class functions would help so that some of those functional objects can feel a little bit more better situated within the language but you know we work with the tools we have and you know we've probably all been writing with Ruby for a long time and we're here because we still enjoy it as a language for all of its good features and all of its kind of quirks and I think the fact that it lets us model systems along these lines is great and we can start to develop the same instincts for how to break things down that that other functional languages give us and I think the biggest thing that helped me in doing this was really approaching this completely apart from the tools I was used to using and starting fresh on some new projects so I couldn't fall back to my old habits or those old shortcuts and that was what helped cement the change in mindset and that can still all be done within Ruby Hey, so the dry stack consists of a lot of different small libraries would you agree that it is designed and it is meant for more advanced programmers? I feel that when beginners come and try to learn something then it's difficult for them to decide what they actually want they usually want to have a set of tools that that is like, you know, like packed together for them so I'm wondering if you think that this is meant for advanced programmers or do you think that maybe the community will come up with some like, you know, just the default stack that you only modified if you know what you're doing Yeah, you're right in that the gems are fairly low on opinion they're meant to be flexible and they're meant to integrate into many different situations which is why perhaps they don't guide you as much as other tools but I would say that new developers can become productive with this we saw that at ISLAB we had someone who came on board as a junior developer he'd done some online Rails courses and he was pretty productively working with this because it really is a much simpler concept that is core you create objects you compose them and that's how you get larger pieces of functionality it's actually a smaller number of things to learn and as long as they're guided by some high level organizing structure with an app so if they're working with someone else you can take care of that by having conventions within your app that you've established and I think as part of doing all of this you get better at actually becoming a programmer so instead of being a programmer for one framework for instance you just get better at modeling and solving problems in general and I think that's a great thing for a junior developer or a less experienced developer to learn because they're going to be things that will help them for the rest of their programming career even if these tools fade away the ideas behind them are enduring anyone else sorry I'm interested by your statement that the various dry dot RB gems are intended as atomic because when I've looked at it for it feels almost like you're trying to build a functional DSL using Ruby as an interpreter and while that isn't my taste I think it's a reasonable project and so I guess how do you visualize people just using individual bits and pieces as a drop-in because it seems like a kind of coherent system that you either buy into or don't buy into to me it's true that we sort of have built these to work in harmony as a collection because they share similar philosophy across all the gems but a lot of them are intended the entire idea is to make one each something that solves an individual problem. So I didn't mention it here but one of the ones that's seen the biggest uptake is dry validation which is a standalone validation library and strict rigorous data validation is something that everyone needs in their projects and dry validation is really just about creating schema objects that you can call anywhere in your application so it doesn't really have any requirements beyond that your application can take any shape people are using it in Rails apps people are using it in apps that are the full collection of dry RB gems and because it was done as a standalone thing it can find usefulness in a broader context. Now some of the dry RB gems are a little bit more specific for following an architecture like dry system if you're not going to want to build an app with a container to vend your objects there's not really much point in using dry system but that's the whole idea because we didn't bundle that into everything and make them tied together you can just pick and choose what you'd like to use. So the best ones that we've seen with standalone usage are yeah dry validation and dry types and dry struct because everyone has a need to validate data and model data and treat data with rigor and that's something that can fit with any application. Okay I think that's all the time we have for questions let's give Tim another round of applause guys. Thanks very much.