Summarized using AI

Functional Architecture for the Practical Rubyist

Tim Riley • June 22, 2017 • Singapore • Talk

In this talk, Tim Riley presents an innovative approach to developing Ruby applications through functional architecture, emphasizing a shift from traditional object-oriented design to a functional mindset. Drawing from his experience as a developer at Icelab, he highlights how applying functional programming principles can enhance Ruby applications' design, leading to clearer, more maintainable code.

Key Points Discussed:

  • Challenges of Object-Oriented Design: Riley reflects on the difficulties developers face when applying object-oriented principles like SOLID, which can lead to messy and hard-to-maintain applications. He emphasizes that while such principles are essential, they often lead to design problems if not applied rigorously.

  • The Need for Change: The speaker argues that a practical approach often prioritizes swift feature builds over sound architecture, resulting in harder application maintenance over time. He introduces the concept of functional architecture as a solution.

  • Functional Architecture Principles: He encourages viewing applications as series of transformations where data flows through various functions. This method results in more manageable code structures, contrasting with traditional class-focused designs.

  • Implementation of Functional Objects: Riley explains how creating functional objects that do not mutate external state can simplify testing and enhance clarity. By passing dependencies and using immutability concepts, developers can avoid unexpected mutations in their applications.

  • Integrating Functional and Object-Oriented Approaches: By blending functional programming concepts with object-oriented principles such as dependency injection and composition, Riley contends that developers can adhere to excellent design practices while leveraging Ruby’s strengths.

  • Testing Approach: Using examples, he demonstrates how functional objects facilitate testing. Injected dependencies allow for easy mock implementations in tests, helping isolate features for verification.

  • Adapting Legacy Systems: Riley discusses how developers can gradually transition existing applications to a functional style by incorporating new functionalities and models within Rails or other frameworks—without losing existing operational capabilities.

Significant Examples:

  • Riley uses an imaginary online store's import products class as a primary example to illustrate functional objects' implementation in a real-world scenario.

Conclusions and Takeaways:

  • Functional architecture allows developers to produce cleaner, more maintainable code in Ruby, leading to applications that feel more solid and are easier to extend.
  • By restructuring applications through this functional lens, teams can respond more effectively to changes, reducing integration issues and unexpected behaviors.
  • Riley’s enthusiasm reflects a positive perspective on shifting towards functional programming in the Ruby community, encouraging others to embrace these principles for better development practices.

Functional Architecture for the Practical Rubyist
Tim Riley • June 22, 2017 • Singapore • Talk

Speaker: Tim Riley, Partner and Developer, Icelab

We build our Ruby apps with the best of intentions, but it's all too easy for them to become tangled and hard to maintain. If you've reached for object-oriented design principles as your path forward, and found them elusive or hard to apply, there is still hope! It turns out that some of our best OO code may live behind an FP curtain. Come along and discover how a functional architecture can make your Ruby apps not only SOLID, but a real joy to build, test, and extend.

Speaker's Bio

Tim Riley is a partner at Australian design agency Icelab, and a core developer of dry-rb. He's excited by small libraries, first-class functions, and pushing forward web app development with Ruby.

Event Page: http://www.reddotrubyconf.com/

Produced by Engineers.SG

Help us caption & translate this video!

http://amara.org/v/8HYV/

Red Dot Ruby Conference 2017

00:00:04.620 all right hello everyone I'm Tim commuter sweet from Australia or
00:00:11.620 I work for a company called ice lab and I'm also a core developer in the dry
00:00:16.840 bein Rama B Ruby projects and it's really great to be back in Singapore we
00:00:23.140 love this city it's a great weather great food and thanks it is my whole family this is us here we're here for
00:00:29.769 last year's conference we had a great time our daughter was 1 and my wife was pregnant and here we are now back again
00:00:36.719 my daughter first daughter is 2 years old second one seven months and I was
00:00:41.829 thinking about this and I'm actually scared about what might happen if I come back next year we have space right angle keeps in the
00:00:48.370 family and onto the technical things I'm sorry to say I've got nothing to add to
00:00:53.980 matters comments about every end or JIT compilers except to say that I'm glad he is taking care of it I'm here today to
00:01:01.660 talk about something a bit different building applications with review and I want to talk about a big shift in the
00:01:07.479 way I've been writing rubia I want to talk about functional architecture and I
00:01:13.869 wouldn't start our journey into functional architecture strangely enough with object-oriented design that's that
00:01:20.530 thing we should all aspire to as rubyists right we had these object-oriented design
00:01:25.659 principles like solid to guide us and we know these things are important and that they should influence the way we write
00:01:31.330 our code we've been hearing this for years and myself I'd watch talks about these things too and I say to myself
00:01:37.150 yeah this is how I should write my code and then I'd go back to whatever I was
00:01:43.000 working on and quite frankly it was a mess by comparison and I think my
00:01:48.310 problem was that in my mind I tend to opt for pragmatism over design and I'd
00:01:53.710 reassure myself and I'd say this situation is ok I'm a practical developer I've got work to do what
00:01:59.950 architecture but not surprisingly the longer I worked in these apps the harder
00:02:05.170 and harder they became to change and that's really the point of architecture and design that applied to code these
00:02:11.980 things aim to make change easy and a well-designed codebase it should stand the test of time and we can see
00:02:19.720 these things illustrated through martin fowler's design stamina hypothesis we can lay it out on a chart on the bottom
00:02:26.170 axis we have time on the Left we have features built and then if we start an
00:02:31.960 app without paying much attention to design we may get a really nice initial spike in features but over time that
00:02:37.750 curve kind of leveled off as it becomes harder and harder to build new things but on the other hand if we pay a bit
00:02:44.290 more attention upfront 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
00:02:50.650 new features so we see here we have this line between them this design payoff line and I think my problem was I spend
00:02:57.550 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
00:03:04.750 is a mindset that's perhaps endemic in our community in general because we found our fame in enabling those
00:03:11.440 early-stage productivity wins my problem was also that I wasn't comprehensively
00:03:18.610 applying better designs of my applications some band-aid approaches here and there they might help but
00:03:25.030 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
00:03:33.070 always predict where change will come from what we want ideally is a good
00:03:38.110 design that covers our in their entirety so that's why I'm here today so everyone
00:03:44.440 stand back about something to say functional programming will save us well
00:03:51.600 maybe not quite like that this is the Ruby conference after all and I know
00:03:56.680 many of us 20 were different languages but I can still consider myself happy with Ruby and a practical rubyist and
00:04:02.260 likely Ruby itself is a practical malleable language and it's going to allow us to model things nicely along
00:04:08.740 functional lines - the first step towards a functional architecture is a
00:04:13.780 change in mindset it's about viewing your app as a series of transformations we did our initial data HTTP request for
00:04:21.790 example and then we can run that through a number of transformative steps with each one passing some data to the next
00:04:27.400 until at the other end we get some HTML so in another light an app model is a
00:04:33.729 series of transformations is really an app model is a series of functions and
00:04:38.880 this is where we get to the functional part of functional architecture we can
00:04:43.990 model these functions nicely in Ruby as functional objects let's take a look at one now so for exit our example today
00:04:50.889 we're going to be making an import products class say we're building some kind of online store we use something
00:04:57.370 like this to populate our M product database from some third party feed of products and even in this clewd right
00:05:04.120 here we see the first quality of a functional object usually we can name them by word and then it has a call
00:05:11.979 method just like Ruby's own language level procs occasionally we might want a few more methods at most of the time
00:05:17.949 call it enough that call method accepts an input goes to work on it and return
00:05:25.539 some output now during the lifetime of that method it shouldn't mutate the state of the object and it shouldn't
00:05:31.360 mutate the input data so this is one of our big departures from a classical object-oriented design approach our
00:05:38.289 functional objects separate data from behavior let me keep that data anywhere
00:05:44.320 in their own state before we go any further let's actually take a quick look
00:05:49.539 at what this method will actually need to do the first thing it will do is get
00:05:54.669 a list of product records by downloading that feed and then it will work with a products repository and we'll loop
00:06:01.449 through to the products and create a record for each one in our own database now is it pretty clear and simple method
00:06:07.210 to read and that's because it delegates a lot of its work to some dependencies these are what allow us to break down
00:06:13.720 our application into smaller more useable objects here those dependencies
00:06:18.849 again we have download feed and we have a products repository and from here we
00:06:25.360 can see the next trait of a functional object those dependencies are passed in by the initialized method and then we
00:06:31.840 capture them in the object state and once these are set they never need to change and what you're seeing here
00:06:37.360 really is constructed appendixes it's one of those classical object-oriented design techniques and it works great with these functional
00:06:43.310 objects so the end result is that we can initialize this object just once and call it many times over and once it's
00:06:50.960 constructed if you then go on any passed around to become the dependency of other objects too now you might be thinking
00:06:57.860 what we've seen so far is really just one of those service objects that we occasionally sprinkle around our apps
00:07:03.740 where we see hotspots of complexity it and you'd be right this is one of those things but what I'm proposing is that we
00:07:10.730 use this approach to build out the entirety of our applications functionality because we want that
00:07:17.120 comprehensive design approach so all of that crud that we used to building well they can be functional objects too so
00:07:26.360 that's how we take care of the behavior side of things what about data how do we
00:07:31.430 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
00:07:37.910 types that we might expect another functional programming languages instead what I'm really talking about is
00:07:42.980 modeling these as value objects what is the value object it's just something
00:07:48.200 that holds a particular structure of data that's unique to our business domain and then once we have these
00:07:53.690 objects created we should treat them as immutable once they're initialized their state should never changed to and of
00:08:00.260 course we can add behavior to these objects in classical object owners style with extra methods working with the
00:08:06.140 values we already have and the nice thing about these being simple value objects is that we can then easily pass
00:08:12.560 them around all of our system and not have to worry about unexpected mutations or any sort of behavioral side effects
00:08:18.440 along the way and in this way the value objects become first-class objects
00:08:24.020 within our system all about functional objects can depend upon and work with these structures so in a ruby app built
00:08:32.840 with this style objects tend to break down into these two different things values and functions our values hold
00:08:40.730 data and those functions operate on the data our values are inert they're
00:08:47.300 passive but the functions they're active they go to work on those values and if the
00:08:53.720 values are the content in our system the functions form the pipeline through which the content flows in order for us
00:09:00.860 to achieve some kind of outcome so that's the breakdown values work out to
00:09:09.470 be pretty typical of the dieted code but let's take a closer look at those functions if we built an app around them
00:09:15.530 how does that design approach measure up well I think first thing we should acknowledge that with Ruby being
00:09:21.920 grounded in the object-oriented world and with just using objects to model our functions we should acknowledge that
00:09:27.800 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
00:09:34.490 some of those long-standing object-oriented design principles and I did mention solid before so let's see
00:09:41.210 how that plays out here here we have those five familiar kind of dense little
00:09:46.730 principles there coined there from Robert Martin and they're all concerning class design and together they can
00:09:53.300 actually work as a decent indicator of what is well-designed object-oriented code so we'll step through them now we
00:10:00.320 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
00:10:06.500 well how that our example here it's functional so it's named after a single verb it's got a single responsibility
00:10:12.560 running the import for our products so there's no room really for anything else there if you want to add other behavior
00:10:19.190 that goes somewhere else so I think we're off to a good start we'll give us all the tea the next one the open closed
00:10:26.420 principle says that incidence should be open for extension closed for
00:10:32.630 modification or in other words when we're building a system and you want to introduce new behavior what we should do
00:10:40.220 is rather than modifying the old objects we should create a new objects that work with the ones we already have so this
00:10:47.990 plays nicely with our single purpose functions let's say for example that we want to send an email notification at
00:10:53.300 the end of our import well given that our existing import products function is closed for modification
00:10:59.450 we can extend its behavior by writing a new standard import notification object and then wrapping them both in a
00:11:06.410 higher-level coordinating object that runs the input first and then sends a notification and the beautiful thing
00:11:12.530 about this approach is that the more of our code we leave untouched the more confident we can be that our system will
00:11:18.680 continue to run bug free even as we go and introduce new changes so that's
00:11:24.500 another tick and now we come to the list of substitution principle and this one
00:11:30.260 is really specifically about sub plotting and ensuring that a subclasses interface doesn't break the
00:11:35.900 compatibility with its parent interface and since we mostly rely on competition we don't subclass much at all I'm just
00:11:42.890 going to chalk this one up as an easy win I'll set those those base lines for
00:11:48.380 myself here so from now to the interface segregation principle and this one says
00:11:55.310 that many client specific interfaces are better than one general purpose interface or in other words when you
00:12:02.510 depend on an object and it has interfaces for many different users you wind up being affected by changes to
00:12:09.470 that object forced upon it by those other users well in practice let's see
00:12:14.900 how this plays out we have our import products function we've been working with and it has that download feed
00:12:20.090 dependency where it gets the product information from a remote feed source and this object at the bottom here has
00:12:28.160 effectively been working as an incoming product source but what if we wanted to go and add a different way of getting
00:12:34.580 product information say by allowing users to upload a feed file directly well since that is also about incoming
00:12:41.780 products should we go on add that extra behavior right down to our existing objects or we could but that would
00:12:47.990 violate this principle because it returning the object into a bigger general-purpose interface and that means
00:12:54.020 it's likely going to change for different reasons over time and upset the stability of our app so instead
00:12:59.990 because you've designed our objects to work really easily with different dependencies is much easier and much
00:13:05.180 more natural to model this as a separate object with a new client specific interface purely for the purpose of
00:13:11.300 handling dublin's and this means that other objects can work with it as required so
00:13:16.399 nothing should become unexpectedly unsettled by changes to either of these
00:13:21.449 dependencies so for ticks down one to go we've got the dependency inversion
00:13:27.089 principle and in some states that one should depend upon abstractions and not
00:13:32.160 concretion or in other words we should care more about interface rather than
00:13:37.680 implementation and this is another place where our functional objects really shine because they're re-entered around
00:13:44.250 dependency injection all those things do come in as abstract objects just like
00:13:50.040 this our import products function doesn't know the concrete implementation details of that downloader just its
00:13:56.550 interface just that it responds to call and that is except the feed object this is really Ruby's duck typing in a
00:14:02.910 nutshell because it gives us the flexibility to adjust the implementation details of this object whenever we need
00:14:09.600 or even replace it entirely and as long as we retain that interface everything
00:14:14.670 stays good so how did we do well it's looking like we're solid and what this
00:14:23.309 shows is that by following a functional approach we've actually created better
00:14:28.759 object-oriented code for ourselves and we've done this with a blindingly simple
00:14:33.930 approach really to our design model our behavior this functions I mean we
00:14:40.259 satisfy those principles so easily you just have to think about why they were there in the first place and I think
00:14:46.740 it's because it's so easy in classic object-oriented design to get off to what few like a good start and then go
00:14:53.759 down the wrong path and wound up with something this Vaid I'm specified hard to reason about or
00:14:59.370 just really complicated so we need these principles to try and keep us on track
00:15:04.490 whereas with our functions they're dead simple and they take care of so many of those design considerations for us and I
00:15:13.470 think this approach is confirmed by how we would test these as well let's take a quick look at that we start by recalling
00:15:21.000 those two dependencies who are using download feed and products repository and then we set them
00:15:27.110 up as mocks or test doubles then we can create our object under test the import
00:15:33.380 products object by passing those doubles to the constructor then create a real feed value for our input and then return
00:15:42.230 fixture data from a local file whenever our fake download object is called with the feed and then finally we get to our
00:15:48.740 test so we can call our importer and assert that our repository has been asked to create a record for each of the
00:15:54.200 products in that fixture file and that's it we could use these mocks so easily
00:16:00.500 because our class was designed around working with injected dependencies so
00:16:05.540 instead of having to interact with these two different external systems this remote HTTP datasource and local
00:16:12.649 database instead we could simulate their behavior focus on our one function under test and get the test done quickly in
00:16:19.490 real isolation and with a minimum of fuss I think there's been a strong theme
00:16:26.450 so far it's been about making our dependencies clear and I think this is
00:16:33.620 because it's one of the best things that we can do to enable well-designed code
00:16:39.339 making our defensive dependencies clear means that we can consider the stable dependencies principle as we go about
00:16:46.190 and build our apps and this one says that a stable object an object's
00:16:51.529 dependencies should be more stable than it is what does this actually mean what the state will mean here or a stable
00:16:57.649 object roughly means hard to change and an unstable one is one that's easier to change and every application will need
00:17:04.459 its share of unstable objects because applications do need to change but we want to avoid the situation where the
00:17:11.059 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
00:17:17.839 our dependencies and arrange them according to this principle and make sure the lines flow in the right direction from unstable to stable
00:17:27.530 and we can take this a step further once we start thinking out about our dependencies in this way we can extend
00:17:34.550 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
00:17:40.880 the dependency relationships at the edges and historically the way we built
00:17:46.790 our ruby apps has been really class focused that we consider those classes the nodes to be the primary determinants
00:17:52.430 of what is a well-designed app but I suggest we open things up a bit and take
00:17:58.520 a hard look at the edges in our graph those dependency relationships if we find the edges being arranged like this
00:18:04.190 so our application looks like a big directed acyclic graph this is a good
00:18:09.410 sign that this app is going to be easier to change whereas on the other hand if our graphs had edges flowing in the
00:18:16.400 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
00:18:22.700 going to be more resistant to change and that's really why we have all these printables in the first place they're
00:18:28.670 there to help us achieve apps that are easier to change and well it seems like
00:18:35.090 a functional approach to design gives us this and it does so in a way that's easy
00:18:40.430 to follow based on that widespread use of functional objects and clear composition of dependencies and it's a
00:18:47.480 pretty neat trick I'd say but I don't think it's a tree not a party tree because it doesn't fall
00:18:53.330 down under pressure this is an approach we like so much at ice lab where our work that is now our preferred way to
00:18:59.240 build ruby apps and over the last two years we've built more than 10 apps like
00:19:04.640 this across different domains and with different levels of complexity and I can tell you this it works really well and
00:19:11.230 from this simple reorientation around functions we've started to see a new
00:19:16.250 high level architecture start to emerge because when we have many small single
00:19:22.970 responsibility objects well at that point we need to take time to devise a sensible and consistent approach to
00:19:29.240 naming them and organizing them and that leads to an emergent modularization of
00:19:35.420 code within our system as we see those modules emerge we can start to take note
00:19:41.210 of their boundaries and from this we can start to see you distinct subsystems
00:19:47.690 become more visible in our apps how did this actually play out well we start
00:19:54.200 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
00:20:01.190 shared high level goal so we can formalize that and turn it into a
00:20:06.230 distinct subsystem and as we go other subsystems start to emerge as we
00:20:11.539 develop the features of our app these are the things that make up our applications core behaviors and because
00:20:18.289 we've started paying attention to boundaries we can also take note of the applications own outer boundaries as
00:20:24.320 well so rather than being deeply tired into the app as a whole systems like the
00:20:30.559 HTTP interface the persistence layer and integrations with external services well
00:20:37.039 they can be all carved off and placed at the outer edges of our app and formalize with a clear public API and clear and
00:20:44.360 obvious points of integration and this arrangement this is what makes changes
00:20:49.640 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
00:20:58.909 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
00:21:05.210 out on your own and forging your own path to this because we've seen a whole Ruby ecosystem that's grown up around
00:21:11.840 this approach we have Ramar be a persistence toolkit designed from the
00:21:17.419 ground up to work with a separate layer from your business logic and then we
00:21:23.419 have the drive-e project which is the collection of gems built to enable specifically this design I've been
00:21:29.179 looking at today driver gives us things like type definitions data validation
00:21:35.270 view rendering all models of functional objects it gives us the ability to
00:21:41.330 consistently model both in success and failure results of our functions which lets us compose them in interesting ways
00:21:47.980 and on top of all of this we get nice pattern matching support so we can elevate failure handling through their
00:21:53.450 first class concept within our system and we also get support for building our own value
00:21:59.190 objects with strict data attribute types so it can be confident about what they'll hold now all of those things are
00:22:06.570 worthy of their own talks but I just want to draw attention to one gem here drive system this is built specifically
00:22:12.510 to help us create acts centered around functional objects and what it does is
00:22:17.880 offer us a central container through which we can access all of those objects in our system that container populate
00:22:26.010 itself based on some simple convention that starts by scanning our source files an n for each file registers a matching
00:22:33.480 container identifier and then when we resolve that identifier we get a pre
00:22:39.060 initialized instance of that object ready for us to use with all of those dependencies automatically provided so
00:22:46.050 if you're looking at our examples before and wondering well how do I get everything into the objects this is something that will let you do it easily
00:22:52.410 and it let us do this by giving us this simple mixing that we can use in our
00:22:58.080 class files where we specify our dependencies by their container identifier so instead of having to
00:23:04.260 manually build that constructor and set the instance variables like we just did before we can now just declare our
00:23:09.390 dependencies upfront and the rest is done for us and with all that in place
00:23:14.510 we can now make an instance of our object directly without supplying any arguments at all that initializer and
00:23:22.010 again when we do this all those default dependencies get provided from the container now if you think about testing
00:23:30.720 this opens up to new possibilities for us because we can now choose which of the dependencies we want to leave as the
00:23:36.270 default and which we want to replace with something like a test double so instead of that arrangement like we've
00:23:42.420 had before where we had to provide a double for every single one of the dependencies what we can do now is we
00:23:48.690 could choose not to provide one of them just like here we can choose to skip passing our products repository as a
00:23:53.790 test double so the resulting object here will use just one double and then it
00:23:59.010 will use the real system object for the repository so if we went to test this object it would actually write all the
00:24:05.280 records to our test database and then we could go in a search for their presence afterwards now this is a
00:24:11.039 much less pure approach to testing but in certain situations it may be much
00:24:16.169 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
00:24:24.059 architecture for Ruby apps how did we do it well we really created a hybrid of
00:24:30.690 functional and object-oriented programming based on some really simple rules we separate data and behavior and
00:24:37.200 then we provide that behavior as functional objects but we also drawn the best bits of object-oriented programming
00:24:43.679 like dependency injection and object composition and by doing this we found
00:24:48.869 an approach that's easy to follow and makes for apps that are easy to change
00:24:54.409 and I think this makes a functional architecture a real practical choice for any rubyist out there and thanks to
00:25:02.279 organizations like ROM are being drive ADA's already support in our community for following this approach and also by
00:25:08.549 choosing to stay with Ruby and take an approach like this rather than trying to follow it with some other programming
00:25:13.799 languages you get to continue to leverage all of your existing knowledge of our community our ecosystem and our
00:25:20.159 tools so given all that I think functional architecture is really worth
00:25:25.769 the try I think you can help you build better things because while solid might
00:25:31.440 be asking him for a collection of design principles I think it's a feeling too
00:25:36.690 and I know that since I've been working in this way I've been shipping apps that feel 100 percent solid absolute I'm
00:25:43.049 confident will work the way they shed an absolute I know I can go back and easily change later now I feel that taking this
00:25:49.649 journey has made me a better developer but most importantly it made me a happier developer I'd love for you to
00:25:55.769 join me and if you would like to join me now's a good time to check out this
00:26:01.679 repository because we're just getting started in building a best practice example app that follows all these
00:26:06.869 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
00:26:13.460 and with that thanks very much for listening and I hope you give this a pitcher chance thank you very much
00:26:28.660 all right so do we have any questions for mr. timidly anyone yeah
00:26:46.510 hello first thank you for the talk it is really good so my question is how about
00:26:56.130 how do you develop this kind of component I mean for example did you
00:27:03.130 first draw some kind of diagrams before starting to create the objects like
00:27:09.370 import products download speeds and the repositories and you know in which order
00:27:15.460 do you go from bottom up like the protector ease first or you start top-down and putting in tests suppose
00:27:21.640 first things things like that I would like to know about the process which you
00:27:26.860 used to design and develop thank you thanks for the question I guess a top
00:27:33.040 down or bottom approach bottom-up approach is there for you to take depending I guess on your personal style
00:27:39.160 and how much you know about the requirements up front so if you were sort of doing exploratory development or
00:27:45.340 your requirements for fuzzy definitely you'd start with a top-down approach you'd make your highest level thing and
00:27:51.610 then you start to explore what it has to do and as you notice studying that objects starting to do too much that's
00:27:57.910 when you break it apart and build extra objects as dependencies and because we
00:28:03.010 we work with that auto-injector mix in is the way we work that's a really low
00:28:08.140 friction thing to do there's not much effort to make a new file and then check and identify for that file in there's a
00:28:13.330 dependency so that's some that's one way to take it or on the other hand if you really knew exactly what you wanted to
00:28:19.030 build you could start with yeah the solid underpinnings and then go upwards so either options there it just depends
00:28:26.320 on the situation of the app that you're developing I think can I have one more
00:28:33.070 question okay thank you so sometimes
00:28:39.400 when when going this way do you have the feeling that when you want to implement some features you have to change you
00:28:46.030 have to make change to the several files instead of one file for for example if I want to like add something to import
00:28:54.700 products like it needs a new behavior like aggregating data from repository but the repository doesn't
00:29:01.670 have that functionality yet and in the end adding one thing requires changing
00:29:07.820 in multiple files yeah do you have that clearly yeah this is something that
00:29:13.160 definitely happens but I think it's an advantage because think about the opposite if you had all of your behavior
00:29:20.030 clustered into one class and that means that much more of your system is all
00:29:26.030 using that one class and then it means that any change you make there is going to have ripple effects ones that you may
00:29:33.080 not even expect whereas with smaller responsibilities broken apart into a larger number of
00:29:40.490 individual files you can see much more clearly because of the dependency relationships what is using what and you
00:29:47.060 know what each one should be doing specifically because it has that single responsibility and in some some objects
00:29:54.290 that do have sort of wider api's like repositories where you might want to add lots of methods for different ways to
00:29:59.840 access your data because those methods are clearly named by you the developer you've explicitly made an API you should
00:30:06.350 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
00:30:13.070 jump across a few different files rather than the smaller number of larger bulkier files
00:30:21.730 there anyone else
00:30:27.870 no I'll write up there oh yes oh okay could you approach hold microphone
00:30:44.710 okay hello so my question is if I want
00:30:50.060 to migrate an existing application happen running production application so
00:30:55.250 what would put a complicity to move it from Opie to functional programming APIs
00:31:03.040 that is a tricky question I guess dealing with any legacy application comes with a whole bunch of nuance but I
00:31:10.730 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
00:31:16.790 approaches to the design and I guess commit to the new approach whenever you
00:31:22.820 add new functionality I mean you can certainly use drive system within rails for instance as a rail pipe forward what
00:31:30.530 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
00:31:38.690 other hand these are all making a web app with with dry web and any other web front-end these things are alright
00:31:44.810 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
00:31:51.650 sort of separate recommendable app alongside but that's quite an extreme
00:31:56.780 approach so I think a more sensitive approach would probably be just to start
00:32:02.930 thinking about as you build new functionality think about how it can be modeled as verbs and put them put those
00:32:09.440 things those classes in your rails application wherever it fits and start working with those and you can hired for
00:32:15.620 instance active record queries behind query objects you could also hired
00:32:21.070 operations that write to the database behind their own objects and then your active record models for instance then
00:32:26.300 become a much lower level persistence related detail rather than something that you use as the primary API of your
00:32:32.330 app so that kind of mindset you can apply to any tools that you're using whether or not you use these gems or
00:32:37.700 something else so that's how I think you'd do it ok cool do we have more questions yeah
00:32:48.520 thanks for the talk to him and do you think that ruby would benefit from
00:32:54.430 adopting some of the patterns you were showing for example value objects could
00:32:59.480 be part of the Ruby core basically and people would probably pick pick up this
00:33:04.640 pattern way easier you know this thinking of like okay I have an immutable read-only value object if it
00:33:12.170 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
00:33:21.500 this afternoon no I think masters point about compatibility is a really good one
00:33:27.020 there um we can do these things in Ruby by convention an object can be immutable
00:33:32.480 within our system if we choose not to mutate it and if we choose not to create writers sure I think some some
00:33:41.320 facilities around first-class functions would help so that some of those
00:33:46.490 functional objects can feel a little bit more better situated within the language but you know we work with the tools we
00:33:53.330 have and you know we've probably all been writing with Ruby for a long time
00:33:58.730 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
00:34:04.880 and I think the fact that lets us model systems along these lines is great and
00:34:10.690 we can start to develop the same instincts for how to break things down that that other function languages give
00:34:15.919 us and I think the biggest thing that helped me in doing this was really
00:34:21.909 approaching this completely apart from the tools I was used to using and starting fresh on some new project so I
00:34:28.580 couldn't fall back to my old habits or those old shortcuts and that was what helps submit the change in mindset and
00:34:35.450 that can still all be done within Ruby
00:34:42.560 so the dry stack compact consists of a lot of different small libraries would
00:34:48.899 you agree that it is designed and it is meant for more advanced programmers I
00:34:53.909 feel that when beginners come and try to learn something the in difficult for them to decide what they actually want
00:35:00.840 they usually want to have a set of tools that that that is like you know like
00:35:06.270 pack together for them so I'm wondering if you think that this is meant for advanced programmers or do you think
00:35:11.550 that maybe the community will come up with some like no just the default stack that you only modified if you know what
00:35:17.220 you're doing yeah you're right in at the gems are fairly low on opinion they're meant to be flexible and they're meant
00:35:23.010 to integrate into many different situations which is why perhaps they don't guide you as much as other tools
00:35:28.580 but I would say that new developers can become productive with this we saw that at Ice lab we had someone who came on
00:35:36.930 board as a junior developer he done some online Rails courses and he was pretty productively working with this because
00:35:42.570 it really is a much simpler concept that is core you create objects you compose
00:35:47.580 them and that's how you get larger pieces of functionality it's actually a smaller number of things to learn and as
00:35:54.360 long as they're guided by some higher-level organizing structure with an app so if they're working with
00:36:00.180 someone else you can take care of that by having conventions within your app that you've established and I think as
00:36:05.730 part of doing all of this you get better at actually becoming a programmer so instead of being a programmer for one
00:36:10.770 framework for instance you just get better at modeling and solving problems in general and I think that's a great
00:36:15.990 thing for a junior developer or a less experienced developer to learn because they're going to be things that will
00:36:21.330 help them for the rest of their programming career even if these tools they'd away the ideas behind them are
00:36:26.490 enduring
00:36:33.140 anyone else oh sorry I'm interested by
00:36:41.700 your statement that the various dry dot RB gems are intended as atomic because
00:36:48.330 when I've looked at it before I feel almost like you're trying to build a functional DSL using Ruby as an
00:36:55.440 interpreter and while that isn't to my taste I think it's a reasonable project and so I guess how do you visualize
00:37:05.070 people just using individual piece bits and pieces of the drop in because it seems like a kind of coherent system
00:37:10.830 that you either buy into or don't buy into to me it's true that way you sort
00:37:16.110 of have built these to work in harmony magic collection because they share similar philosophy across all the gems
00:37:21.840 but a lot of them are intended your entire idea is to make one each something that solves an individual
00:37:27.720 problem so I didn't mention it here but a one of the ones I've seen the biggest uptake is dry validation which is a
00:37:33.990 standalone validation library and strict rigorous data validation is something
00:37:39.150 that everyone needs in their projects and dry validation is really just about creating schema objects that you can
00:37:44.730 call anywhere in your application so it doesn't really have any requirements beyond that your application can take
00:37:50.580 any shape people are using it in rails apps people using it in apps that are the full collection of drive idioms and
00:37:57.119 because it was done as a standalone thing it can find usefulness in a
00:38:02.640 broader context now some of the dryer B gems are a little bit more specific for following an architecture like drive
00:38:10.590 system if you're not going to want to build an app with a container to vend your object there's not really much
00:38:15.690 point in using drive system but that's the whole idea because we didn't bundle that into everything and make them tied
00:38:21.420 together you can just pick and choose what you'd like to use so the best ones that are that we've seen with standalone
00:38:27.240 usage dry validation and dry types and dry struct because everyone has a need to validate data and model data and and
00:38:36.600 treat data with rigor and that's something that can fit with any application
00:38:44.790 okay I think that's all the time we have for questions let's give him another round of applause again thanks very much
Explore all talks recorded at Red Dot Ruby Conference 2017
+12