Big Ruby 2013
Services and Rails: The Shit They Don't Tell You
Summarized using AI

Services and Rails: The Shit They Don't Tell You

by Brian Morton

In the talk titled "Services and Rails: The Shit They Don't Tell You," presented by Brian Morton at Big Ruby 2013, the focus is on the complexities involved in building services and integrating them into Rails applications. Morton shares insights from his experience at Yammer, where the need to adapt and scale a massive Rails app has led to the development of over 20 services. The presentation emphasizes the importance of understanding both the potential benefits and challenges associated with service-oriented architecture.

Key Points Discussed:

- Need for Services: With increasing application size, a service-oriented architecture allows for components that can scale independently. Larger Rails applications can become unwieldy, leading to obstacles in development.

- Modularity and Reusability: Morton illustrates the separation of functionalities to create a denormalized data store for their search architecture, enabling features like autocomplete to be added without extensive reinvention.

- Loose Coupling Benefits: Independent updating of services is facilitated by their modular nature, reducing the risk of disruption when changes are made.

- Cross-Functional Collaboration: Establishing cross-functional teams to tackle projects fosters innovative solutions and prevents knowledge silos present in vertically or horizontally divided organizations.

- Migration Challenges: As services are built and moved out of the monolithic Rails app, maintaining oversight and ensuring proper support for new services is crucial.

- Monitoring and Performance: Transitioning from monitoring a single application to multiple services increases complexity in performance evaluation and capacity planning.
- Emphasizing Utility Tools: Morton highlights the importance of using effective tools like "Soup Kitchen" for managing development environments and "Deploy" for seamless service addition, ensuring that developers remain productive.
- Cultural Shifts Required: Adopting services involves not only technical changes but also a cultural shift in team collaboration and communication, with an emphasis on maintaining flexibility and learning from mistakes.

Conclusions:

Morton stresses the necessity of understanding the evolutionary nature of software architecture and the importance of being prepared to adapt without clinging to past decisions. Acknowledging that complex systems can fail is crucial, as is the need for a thoughtful and measured approach towards service orientation.

Overall, while service-oriented architectures present significant advantages, they require careful navigation of inherent complexities to be successfully implemented.

00:00:19 All right, so I'm going to be talking about services and Rails. Not necessarily services in Rails, although that's also a great topic. This talk will focus on services and Rails, highlighting some of the key aspects we don’t often discuss. We’ll be looking more at the organizational side of things and some of the challenges that come with it.
00:00:43 My name is Brian Morton, and I work on the Rails team at Yammer. One of the main responsibilities of the Rails team is to identify chunks of functionality that we can extract into services. We collaborate with other teams within the organization to accomplish this integration into our Rails app. Additionally, I have a passion for Zelda, Ruby, and music.
00:01:05 This presentation marks my first conference talk, which I’m really excited about. However, it’s been a while since I last gave a presentation. I found myself searching for the right template to structure my talk. I glanced through some templates from Yammer and Microsoft, which inspired me. I have a pet peeve regarding talks that have titles but fail to address the content suggested by those titles. I wanted to ensure my talk remains relevant to its title.
00:01:40 In one of the templates I found, it suggested using full-bleed photos because they can help set the mood or evoke emotions, making for a more memorable presentation. I thought that was a great idea, so I decided to replicate that with my own slide. This visual will assist us in conveying the message that there are many things about this topic that aren't commonly discussed.
00:02:02 I want to clarify that much of what I discuss today may not apply to everyone, particularly those currently working on a startup. In such cases, many of these complexities can be ignored as you focus on proving your concept. However, once you determine that you need to scale, there will be uncomfortable decisions to make. I’m expecting that some audience members might find this content applicable.
00:02:26 Let’s continue. We currently face the challenge of working with a massive Rails application. It has grown significantly and encompasses over 300 models and 200 controllers. This architecture is supported by more than 20 JVM services, handling over a billion requests each day. Yet, we still find ourselves managing this vast Rails app.
00:02:50 The increasing size of the application creates obstacles as we proceed, although we manage to navigate through it mostly. We’ve learned to chip away at the problem by building services, but there are still significant pain points, especially when dealing with sharding or upgrading Rails. These processes often turn into all-or-nothing projects, which is challenging.
00:03:02 So, why should we build services? What advantages does this approach offer? I want to briefly touch on service-oriented architectures, which I’m sure many of you are already familiar with. One goal is to develop components that can scale independently. When you have well-structured services, it enhances versatility and makes reusability significantly easier.
00:03:43 For instance, as we started to construct our search architecture, we leveraged previous experiences from our team. This enabled us to separate functionalities into distinct components. We created a denormalized data store that operates independently, allowing efficient interaction with other services, including an indexing service and a search interface. This modularity made it straightforward to add new features, reuse existing structures, and minimize friction in development.
00:04:28 When we needed to integrate an autocomplete feature, it drew directly from the same data store and indexing service we had previously established. This reinforced our ability to adapt and add functionality without reinventing the wheel. Our export service, which we call "Slurpie," extracts data from our denormalized store, resulting in more robust and reusable components.
00:05:12 Scalability is another critical benefit of service-oriented architecture. Scaling up a service is more manageable when individual functionalities are decomposed, as we can precisely understand the specific needs and performance patterns of each component. This arrangement allows for smarter resource allocation since it's unnecessary to apply resources uniformly across the entire architecture.
00:05:46 Moreover, the concept of loose coupling is an inherent strength of these service-oriented architectures. With smaller, focused parts, we can independently push updates to each piece without affecting the others. This decoupling allows us to replace libraries, platforms, or entire technologies without causing disruption to our overall ecosystem.
00:06:17 For instance, we are currently transitioning to a new file storage backend that has largely proceeded smoothly. We've been able to replace existing services with new implementations, although there have been some bumps along the road. Nevertheless, these challenges are opportunities for improvement, and we can adapt as necessary.
00:06:55 Another significant goal of adopting services is maintaining codebases that can scale across our organization. We have a robust engineering team of around 150 engineers, and having a monolithic application in such an environment can lead to overlaps and complications. With 300 models and 200 controllers, it becomes increasingly challenging to keep all aspects of the application in mind.
00:07:31 By transitioning to services, we can decouple responsibilities, allowing different teams to tackle specific components. This framework can streamline development processes and reduce bottlenecks. Our goal is to establish systems where teams can effectively collaborate, often implementing dummy interfaces as placeholders until the real components are ready. This method reduces wait times and enhances progress.
00:08:10 However, all of this takes time and cannot be achieved overnight. When you start building an application or startup, these complexities serve as a barrier to swiftly delivering products, which is essential. The single, undivided codebase allows for quick changes and easy access to data layers, contributing to faster outcomes.
00:09:00 But as you begin to build services, you will understand that the comfort it provided can hinder your efficiency. As you learn to build your application, you will discover usage patterns and understand how to divide responsibilities more effectively. It’s a gradual learning process, and you won’t wake up one day with services fully in place.
00:09:39 Sometimes this transition may require organizational changes. A concept we frequently discuss is Conway's Law, which states that organizations optimized to minimize bottlenecks will inevitably create code that reflects the same philosophy. Essentially, the communication structures within teams often dictate how the system is organized.
00:10:14 Many companies divide departments either vertically or horizontally, which can lead to silos within the organization. This structure can make it challenging to maintain fluid communication, which can stifle creativity and decision-making processes. I have an example from our early days at Yammer to illustrate this point.
00:10:44 In our early messaging team, they were responsible for both the messaging service and its integration within the Rails app. They implemented both sides of the technology stack. This arrangement resulted in siloed knowledge, making their work less effective.
00:11:05 As a response, we reevaluated our strategy. We established a Rails team to manage service implementations while coordinating with core services teams to streamline the development process. This approach allows us to iteratively migrate services without centralized knowledge slowing us down.
00:11:32 In supporting the transition towards services, we’ve adopted cross-functional teams for projects. When building new features or services, we take individuals from various functional teams relevant to the project. Collaboratively, they focus on a project for a limited time, allowing them to learn and adapt as they go.
00:12:14 This approach fosters innovation and ensures diverse inputs, enabling the teams to develop well-informed solutions that meet specific project needs. The temporary nature of these teams brings agility to our processes, permitting them to adapt as needed after every project.
00:12:37 While there are trade-offs, such as the loss of siloed expertise, the benefits often outweigh these challenges. Teams frequently learn new domains, which adds some overhead but not enough to dissuade us. We must remain mindful of how we couple APIs in the implementation, especially as mobile clients evolve to require more tailored data.
00:13:15 After a project’s completion and the disbanding of teams, ensuring proper support for the newly developed services remains important and requires strategic planning. We address this by maintaining an internal cross-functional support team to help with the upkeep and ongoing improvement of these services.
00:14:01 As we progress, we must acknowledge that multiple strategies exist for implementing services. A straightforward approach places services behind Rails, allowing ease of integration without outside communication, which simplifies the process. However, as we advance, we will eventually need to interact directly with services to enhance capabilities.
00:14:35 A prime example involves our service called 'Mugshot,' which manages dynamic image resizing, allowing clients to communicate directly with it. Most of the time, this works seamlessly, but authenticating direct service communications can become an obstacle.
00:15:04 The necessity to interact directly with the database brings its challenges. Reading from the db is manageable, but writing becomes a complex issue. When attempting to work around the Rails layer entirely, it puts our resources under pressure and exposes us to potential issues.
00:15:52 One pressing concern arises from the way Active Record handles data management, often complicating processes in unexpected ways. While it provides a convenient interface and various helpful features, it is essential to disentangle our reliance on it to achieve better performance.
00:16:37 As a solution, we utilize services as indexes to retrieve and store relevant IDs, which we can then integrate through Rails when necessary. We’ve kicked around the idea of developing ‘Bodega Services’ that entirely transform the data layer, allowing us to better manage sharding and caching behind a service-oriented façade.
00:17:20 Once we move data outside of our Rails app, it necessitates duplicating it since our existing services must integrate smoothly without downtime. The risks of service failure and data corruption are particularly concerning. Consequently, we've instituted a double-dispatch method, where data is written to both the database and the service concurrently.
00:18:02 This approach allows us to monitor performance and begin to understand scalability and what load our services can handle. While this method effectively threads the complexity of service orientation, it does lead to challenges involving duplicate data management and ensuring that our teams know which data source to trust.
00:18:39 Managing the overlap of data between various locations can become confusing for developers, and old data often sticks around, creating issues if not addressed promptly. For example, we moved our mock-shot service out of Rails, but some avatars remained within the app, which can confuse developers attempting to understand the systems.
00:19:05 These are trade-offs that come with implementing cross-functional teams. Our teams must ensure that, after they disband, there’s a plan in place to ascertain accountability regarding ongoing support and maintenance. Organizational comfort can make teams resistant to letting go of the past, especially if they’re familiar with the old way of doing things.
00:19:49 Moving entirely to services entails a commitment to facing new challenges without reverting to familiar solutions. We strive to incrementally shed complexity while ensuring a smooth transition, balancing operational needs with the comfort of familiarity.
00:20:37 To keep development streamlined, we must equip our teams with effective tools. Working within a Rails app might provide short-term efficiency, but longer-term goals require careful planning and preparation for the complications that come with service orientation. Providing our development teams with robust tools is paramount.
00:21:15 Our in-house solution, Soup Kitchen, simplifies managing Vagrant environments, allowing developers to focus on work rather than worrying about infrastructure complexities until absolutely necessary. It's vital to ensure developers feel at ease within their tools, allowing them to maintain productivity without unnecessary roadblocks.
00:21:55 For deployment, a cohesive system is necessary to facilitate adding and managing new services promptly and efficiently. We developed a one-click deployment tool we refer to as "Deploy." This tool enables engineers to add new services quickly across various environments while maintaining proper protocols.
00:22:37 One crucial aspect of managing a service-oriented architecture is monitoring and performance evaluation. The shift from monitoring a single app to tracking multiple services introduces additional complexities, requiring more comprehensive capacity planning and testing for reachability across the environment.
00:23:20 To ensure sustainable development, we use standardized tools that help manage and monitor deployment, keep track of interfaces, and maintain consistency across our services. We find value in consistent data formats, which help reduce the overhead associated with differing response structures.
00:24:02 This is a simplified example of how we utilize tools like Dropwizard, a Java library that integrates various essential functionalities to enable quick development cycles. However, it's essential to recognize that our approach may not work for every organization or team structure.
00:25:01 In summary, while service-oriented architectures carry many advantages, they also come with their fair share of complexities and potential pitfalls. Understanding that complex systems may fail is crucial as we design a successfully integrated workflow.
00:25:38 Ultimately, regular evaluation of costs and the viability of the chosen architectural decisions is necessary to avoid stagnation. Acknowledging that the context and needs of your business evolve over time means not clinging to previous decisions.
00:26:19 Don’t scale prematurely, but be prepared for the moment when you need to embrace that shift towards a service-oriented architecture. Building services demands not just a change in coding style, but also a cultural shift in how teams collaborate and communicate effectively.
00:27:05 As you proceed, be kind to yourself and your team—mistakes will happen. We’ve had to rewrite our search stack several times. Still, every setback provided valuable learning experiences to help us refine our approach.
00:27:42 In conclusion, maintaining humility and openness to retracing decisions is essential to successful software development. Adapting to complexity may be uncomfortable, but with careful consideration and the right tools, we can navigate the challenges ahead.
Explore all talks recorded at Big Ruby 2013
+7