RailsConf 2019

Sprinkles of Functional Programming

Sprinkles of Functional Programming

by John Schoeman

The video titled "Sprinkles of Functional Programming" features John Schoeman discussing the integration of functional programming concepts within Ruby on Rails applications. During the talk at RailsConf 2019, Schoeman aims to demonstrate that functional programming can enhance the quality and effectiveness of Rails code, especially for tasks focused on data manipulation rather than object modeling.

Key points discussed include:

- Multifaceted Nature of Ruby: Ruby's capabilities allow for both object-oriented and functional programming styles, and developers should choose based on the task at hand.

- Understanding Paradigms: Object-oriented programming is effective for modeling behavior, whereas functional programming excels in handling data transformation and aggregation.

- Task-Based Approach: Schoeman advocates for assessing how requirements might evolve over time and selecting a programming paradigm accordingly.

- Case Study: Schoeman presents a fictional company scenario illustrating two approaches (object-oriented and functional). The object-oriented approach resulted in increased complexity with a proliferation of classes, while the functional approach maintained simplicity and clarity by emphasizing a data pipeline that was easier to manage and expand.

- Development Velocity: The functional approach led to significantly less code and quicker implementation times compared to the object-oriented method.

- Actionable Insights: The video concludes with recommendations for developers to consider potential changes in requirements and embrace a hybrid coding style that incorporates elements from both paradigms. Resources for further learning in functional programming are provided at the end.

Overall, Schoeman's presentation emphasizes the importance of adapting programming styles to suit developmental needs and how functional programming can streamline the coding process in Rails applications.

00:00:20.810 All right everyone, let's get started. This is my talk, "Sprinkles of Functional Programming." Before we begin, I would just like to express a few thank yous.
00:00:26.400 First off, thank you very much to Ruby Central for inviting me to come speak today. I'm super thrilled to be here and to be part of this amazing Rails conference.
00:00:39.180 Secondly, thank you all for coming and joining me in this first hour of the conference. I'm super excited about this material, which has added a lot of benefits to my programming practice over the past couple of years.
00:00:46.470 I'm excited to be able to share it with you. So I'm John Schoeman. I work at Thoughtbot as a developer and consultant.
00:01:00.059 When I first told some of my colleagues that I had a talk accepted into the RailsConf, the first thing they said was, "All the best RailsConf talks have a Twitter slide." It turns out I don’t have a Twitter, but I still made you a Twitter slide. That is my real GitHub account if you want to find me on the internet, and I just go by my name, John Schoeman.
00:01:20.580 Cool, so let's just dig into the talk. My goal for this talk is to convince all of you that the ideas and styles of functional programming are worth considering and integrating into your Rails practice. This could lead to numerous benefits in terms of qualitative code and the effectiveness of developers.
00:01:41.369 A roadmap for this talk will start with establishing a thesis and key points right at the get-go. We'll talk a little bit about functional programming and object-oriented programming at a very high level. Different people tend to mean different things when they say these terms, and they can be a bit ambiguous. I’m going to explain what I mean when I use them so we can all be on the same page.
00:02:05.099 I’ll make a recommendation that will include a story with some code. That’s where we will spend most of our time today. Then we'll recap and end with an action item for you to take home.
00:02:30.020 One thing to note that’s not included in this roadmap is that I am not planning to talk about implementation details of how to write functionally styled code on a line-by-line basis. So if you have zero exposure to functional programming, that's totally okay. We won't delve into those details, but we will spend most of our time at a much higher level talking about why and when you might want to consider functional programming.
00:03:01.220 At the end, there will be a slide with some resources if I’m successful in inspiring you to learn more on your own. Good starting points for you. Cool.
00:03:36.360 So let's get to the thesis. Hopefully, these are points that we can all agree on. First off, Ruby is a general-purpose programming language and a multi-paradigm programming language. It is excellent at both object-oriented and functional programming. We, as developers, get to choose which style to use.
00:03:54.170 As Rubyists, I'm sure we're all very familiar with concepts such as classes, inheritance, defining methods, polymorphism, and encapsulation. But we also have access to things like lambdas and the Enumerable module. Ruby 2.5 introduced `yield_self`, and Ruby 2.6 has further enhanced data pipelines, making building them even more convenient.
00:04:06.880 The second point is that different paradigms lend themselves to different tasks. Specifically, object-oriented programming tends to be very good at modeling behavior, whereas functional programming is generally better at handling data. Based on these two points, I argue that as Rails developers, we should choose our programming style based on the task at hand, as doing so will lead to a better product.
00:04:40.070 To elaborate, if you're modeling behavior, you should prefer classes and objects. However, if you are handling data, you should leverage data pipelines and functions.
00:05:00.000 Next, let's discuss functional programming versus object-oriented programming. I think most people focus a lot on objects and functions when discussing these paradigms. This is a great way to categorize programs based on their style. However, in my day-to-day development practice, I have found it more useful to consider what aspects of the different styles are difficult to change.
00:05:35.750 To illustrate this, let’s consider how these styles handle changes in requirements. In an object-oriented program, adding a new behavior (a new method) is very straightforward. You can just add that method without needing to change existing code.
00:06:02.399 In contrast, adding data in object-oriented programming can often require refactoring existing code. In functional programming, the opposite is true. Adding new data is typically easy, while introducing new methods may require changes to your code.
00:06:30.630 Some typical use cases for object-oriented programming are anything with modeling in the name, such as users, posts, and interactions between entities in your business domain. These are nouns that you can point at. On the other hand, functional programming is typically used for data manipulation, such as data transformation or aggregation, where you take input data from one point and get it to another.
00:07:17.000 This talk was inspired by multiple instances during my career, specifically in relation to code that I have written and code written by others. By applying these concepts regarding what is easy or difficult to change, I have found that doing so has added a lot of value and resolved many pains.
00:08:00.000 Based on my experiences, I've distilled some recommendations that I would like to share. Given that methods are easy to add in object-oriented programming while data is easier to add in functional programming, we should consider how we expect requirements to change before starting a task.
00:08:21.000 This particularly includes considering what users might want in the future, even though that's a difficult question to answer. If we do that and select our development paradigm accordingly, many good outcomes will occur.
00:08:41.000 To illustrate this, I will provide a story set in a fictional company, where I outline a series of future requirements. I'll first solve them using an object-oriented approach and then revert to a functional approach, creating a choose-your-own-adventure story.
00:09:04.000 At the end of this tale, we'll compare and contrast the results to derive any lessons learned. Additionally, there will be plenty of code, and I will be going through it quite quickly. I encourage you not to read it too deeply, just a squint test to catch anything important. If you're trying to reach 100% comprehension, that might be unreasonable.
00:09:27.000 I will provide a GitHub repo link at the end, so if you want to review the code in detail afterward, it will be available.
00:09:51.000 Let’s imagine a Rails app—a typical sort of Rails monolith. It's a product-oriented business with eight developers on the team and a product manager practicing Agile methods.
00:10:03.000 For the sake of the story, let’s say this is a B2B application that allows small businesses to upload their inventory, which users can review and purchase. Think of it as a mix between Craigslist and a product catalog.
00:10:21.000 Currently, users can come to a products index, fill out forms to create products, and life is good. The company is doing well and gaining new clients regularly.
00:10:40.000 The sales team is currently negotiating with a new client, the largest one they've ever had. However, this new client has concerns about uploading their products, as they have a significant volume and need to change it frequently.
00:11:02.000 They ask if they can import their internal CSV inventory directly into the system. The sales team agrees to this request, sets a deadline, and submits it to the product manager.
00:11:29.000 The work gets added to the product roadmap and incorporated into the next sprint. The developers start working, and they successfully end up with a pull request that allows users to upload product CSVs.
00:11:44.000 Considering their company practices test-driven development, they begin with a high-level feature spec that allows the user to attach a CSV file and upload it. The implementation involves iterating over each row of the CSV, massaging the data where necessary, and saving it to the database.
00:12:09.000 They end up with an imports controller and additional specs. Here's a GIF demonstrating the demo to their new client, who is thrilled.
00:12:26.000 Weeks pass, and everyone is happy: users are purchasing products, and the company is thriving. However, the sales team is soon negotiating with an even larger client, who has a different approach to data management.
00:12:49.000 The new client asks if they can import Excel files, as they do not work with CSVs internally. The sales team reassures them that they can.
00:13:05.000 The developers start working on this new requirement, and first notice that it might be sensible to separate the imports from the product model. They decide to create a service object called Product Data Importer, which will handle this concern.
00:13:26.000 With this architecture change, they will be instantiating a Product Data Importer and passing the file path to its constructor.
00:13:41.000 They notice another opportunity for separation of concerns: pulling out the formatting of the data handling process. Thus, they introduce a new service object called Product Data Formatter.
00:13:56.000 Now they have a cleaner structure, and updating the code to handle Excel files involves simply adjusting the file extension check.
00:14:11.000 Progress continues smoothly, and various commits allow for new features and enhancements. However, they encounter another client who has a different currency format.
00:14:27.000 They need to handle dollar signs in front of their prices, which necessitates more updates and refactoring. The developers continue to refine their approach, maintaining a focus on separation of concerns.
00:14:47.000 As they introduce a dollar sign formatting feature, they maintain test-driven development principles and end up producing a lot of service objects for separation of concerns.
00:15:10.000 As they wrap up, there's a balance of okay performance and client satisfaction, leading to high coverages of their test suite.
00:15:31.000 Now let's compare how all of this would work in a functional programming scenario. Imagine the same team attended RailsConf and were inspired by a talk focused on functional programming.
00:15:48.000 They decide to approach the client requirements with a functional mindset, knowing that behaviors remain constant while data requirements change. They want to separate the importing behavior into a single function.
00:16:06.000 The first thing they do is create a service class that only exposes one public method called import, which accepts a file path. The transition is smooth because they can maintain their existing specs while altering their approach.
00:16:24.000 They quickly set up their data pipeline and separate I/O logic from data processing. This allows them to easily add the ability to import data from Excel without altering any preexisting methods.
00:16:45.000 They ensure their implementation is clean and readable, which in turn helps with testing down the line. With each requirement, their functional approach allows for simple updates, like adding dollar signs to prices.
00:17:07.000 As they maintain their functional pipeline, they find they can quickly address the needs of future clients without contorting their codebase too greatly.
00:17:30.000 The continuance of this functional approach leads to clarity, fewer public APIs, and less code overall.
00:17:44.000 In contrast to their object-oriented journey, they find that they maintain a smaller total number of lines of code despite handling more complex requirements as they progress.
00:18:04.000 They find far fewer complications arise over time, especially concerning public API management. The rapid development velocity and usage of data pipelines clearly contributes to being able to meet business goals.
00:18:25.000 Reflecting on their journey and its usefulness, they learn to be mindful of how changing requirements will affect the choices they make. They also discover that sprinkles of functional programming go a long way in enhancing their development practice.
00:18:43.000 In summary, this talk has been a journey of exploring the differences between object-oriented and functional programming styles, and how thoughtful choices can have a significant impact.
00:19:00.000 It's crucial for developers to consider how requirements might change over time and what paradigms best suit those changes, enhancing both productivity and the quality of the code delivered.
00:19:18.000 I hope you take away that consideration of business goals before starting any task can lead to a superb development process.
00:19:34.000 Finally, I encourage you after this talk to explore more resources on functional programming and consider how you'd like to bring in sprinkles of it into your Ruby development.
00:19:51.000 This can enhance your work and provide different perspectives on handling code. Thank you very much!