00:00:19.730
Hi everybody! I have 30 minutes, and I have a lot of things I really want to talk about right away.
00:00:25.070
I want to start by asking how many people have read either of these books: 'Design Patterns in Ruby' or 'Design Patterns: Elements of Reusable Object-Oriented Software'? All right, excellent!
00:00:37.390
We're going to talk more about these principles today, but if you only catch a few minutes of this talk and then leave, I hope you'll take away the importance I place on upfront design, especially in startups. These principles have been incredibly helpful to me, and I wish I had read these books earlier, as I feel I could have saved myself some pain.
00:01:03.850
My name is Mike Subelsky. I'm a co-founder of a company called Other Inbox, which is based here in Austin. This talk is really about what I've learned while building our product. If I could go back in time two years ago and tell myself everything I was going to encounter and ways to save time, this is what I would share.
00:01:17.479
The first question I was asked was whether I ever run on a treadmill while programming. I do have a treadmill desk, which is now featured on our recruiting page. It could serve as a good excuse to talk about recruiting.
00:01:31.009
I set it up with the right code, but I don't actually use it much. When you're doing a task that requires focus and concentration—like writing code—you have to stop the treadmill. If I'm walking at one mile an hour, I can type comfortably, but as soon as I need to focus completely, I must stop. This leads to hours going by without me even realizing I've been off the treadmill, making me question the practicality of having this elaborate desk. It's a nice gimmick to discuss at conferences, but practically, it hasn't been that interesting. However, there's a whole community online about treadmill desks, so if you're interested, check it out.
00:02:17.270
So, my talk is called 'Ruby for Startups.' The twist is that good code is still good code, regardless of the environment. But what I want to highlight is what's unique about working in a startup compared to other environments, whether it be for someone else, consulting, or doing open-source projects.
00:02:41.990
In startups, you're often building something without a clear understanding of the problem you're solving. You're creating an unknown solution to an unknown problem. This might not be true all the time, but in my experience with Other Inbox and my friends' startups, the initial idea we launched with often ended up changing drastically.
00:03:05.120
Even when you believe you've thoroughly outlined a coherent concept, it can turn out to be simple when you introduce it to customers. You often find yourself off the rails in a way you did not expect. Given the typical constraints in a startup—like limited time and resources—it's common for new ventures to experience this rapid iteration and learning curve.
00:03:29.060
Unless you're spun out from a well-funded company, you're under pressure to start generating revenue quickly to validate your ideas. This scenario shapes how you approach your craft. Eric Ries has a remarkable blog that offers startup lessons, emphasizing that startups are fundamentally learning engines.
00:03:42.530
Writing code in such an environment involves ferocious customer-centric rapid iteration. So, this talk is loosely organized around what I wish I could tell myself about thriving under these conditions. I've boiled it down to two key points: first, I would resist the impulse to just dive into the code without thinking.
00:04:15.170
Instead of rushing, I would spend significantly more time on the whiteboard. Not formal documentation but thought experiments—taking the time to discuss the various ways we could structure our code. As a self-taught programmer, I find there's still not enough accessible material on good design, making it important for anyone looking to improve.
00:04:59.670
Thus, I would encourage myself to invest more energy in understanding design principles, ensuring that the results of my work would be significantly better. The other thing I'd do is compile a long list of mistakes to avoid and best practices to adopt.
00:05:21.990
Now that we have covered some preparatory material, I’ll share ideas from Russ Olsen's book, which draws from the original Gang of Four design patterns book and adds insights unique to our Ruby culture.
00:05:52.890
One of the first principles involves separating what changes from what stays the same. If you've read other software development literature, it might be referred to as design for change. The concept is to isolate general, changeable elements—like formatting for files or I/O formats—into their own modules, using encapsulation and information hiding, which allows you to modify internals without affecting the entire application.
00:06:43.440
For instance, when we started dealing with a heavy volume of mail at Other Inbox, we opted for a simple Queue service from Amazon, which shared work across multiple servers. Initially, one might fine to directly utilize a gem that provides a neat interface to SQS and integrate it directly into the code, especially in the early stages of a startup.
00:07:17.460
However, we learned over time that each component of our application was deeply reliant upon SQS, which started to present challenges as we wanted to scale and manage changes. At this juncture, when Amazon deprecated the original version of SQS, we had to refactor our code completely. Luckily, the structure we established meant changes had minimal ripple effects throughout the application.
00:08:18.780
We created a module called Queue Fetcher that allows anybody to request a queue by simply providing its name, regardless of the specifics of who is providing it or how it is managed. This modularization vastly simplified how we engaged with our queuing system. So, whenever we need to change something, it's done in one place, maintaining stability.
00:09:31.390
Another aspect I emphasize is to keep business logic separate. When we receive messages, we initially had a lot of intertwined functionality that complicated the processing. At one point, we created an envelope module that streams lines of logic for routing messages and made that logic easier to handle and test.
00:10:03.100
A significant principle is to program to interfaces, not implementations. As Ruby developers, this is easier due to duck-typing. For example, if you're unsure about the right name for the fundamental unit of what you manage (like an email message) amidst many varying types (RSS feeds, JSON objects), consider abstracting that title for greater flexibility.
00:10:44.500
Another principle involves preferring composition over inheritance, which I’ve seen pop up in discussions consistently at this conference. Instead of chaining classes through inheritance, consider how you can compose objects to extend their capabilities. An example would be when implementing a feed reader, you might want to instruct it what queuing service it should connect with at runtime.
00:11:54.920
The same applies when we use AWS S3. Various objects need the ability to interact with S3, and rather than redeclaring this behavior through inheritance, we can include a module that automatically provides functionality as needed.
00:12:36.050
The next principle is about delegation; objects can express outward behaviors, while the methods internally refer to sub-objects. For example, an external email account may direct calls to an associated email server; users won't know or need to care about how the server is structured, as they interact solely with the account object.
00:13:12.530
One important lesson is 'you ain't gonna need it.' As programmers, it's easy to gravitate toward novel solutions for future potential issues. However, I caution against prioritizing scalability for non-core features during early development. It's crucial to remain flexible and only incorporate complexity when necessary based on user feedback.
00:14:01.160
In the startup phase, focus on learning about what you are building and understanding customer needs rather than obsessing over performance. Ask yourself if you can create a solution that suffices 80% of the time; that initial work can be enough to validate your concept.
00:14:59.490
With that said, let's quickly cover some specific experiences that I've found detrimental or interesting during my journey at Other Inbox, and I'll be open to questions at the end.
00:15:32.320
A vital realization is the need to separate web requests from processing loads. There are fantastic tools available now, such as delayed_job and SQS, which can aid in moving those burdens off the main web request thread. It is acceptable to conduct minor tasks synchronously at the beginning. Ensure you start considering how to prevent any synchronous workload from blocking the web request.
00:16:44.449
Moreover, I've learned to be cautious about using concurrency. It's often advisable to opt for processes that communicate through a shared bus instead of threading because it's simpler and could accelerate your learning. Despite that, I concur; evented frameworks like EventMachine have been invaluable for managing email sending without blocking web requests.
00:17:22.670
As for databases, try to avoid using them for non-critical data. Consider alternatives for storing data that don't require immediate, transactional integrity. For us, we use Amazon Web Services to keep heavier content, like message bodies, in S3.
00:18:07.450
Taking a step further, ensure that you're leveraging the right indexes for your queries and optimizing your database regularly. It's easy to overlook the importance of how efficiently your queries execute as your application scales to meet user demands.
00:18:51.400
Our team has benefited from upgrading to the latest minor versions of our database software; this has led to performance improvements without needing code changes. Although I like keeping my code organized, sometimes it can get unwieldy the faster you go, leading to messy directories filled with less reusable plumbing code.
00:19:39.620
In managing configuration variables, I categorize them into three groups: those that are immutable and won't change, those that can vary but should remain somewhat solid, and those that require dynamic handling (where users can interact). Utilize constants where possible for the first category.
00:20:23.390
For example, we used to have settings hardcoded to our database. After realizing that we could represent these settings in a simpler form, we made them table-driven configurations that easily integrate into our app and can be adjusted by admins without hardcoding.
00:21:04.090
Another point concerns the avoidance of boolean columns in the database because they can become unwieldy with significant data volume. Instead, consider other options like multiple states, which keep the data more manageable.
00:21:47.400
Presenting code in a way that isolates complex functionality can enhance maintainability. For instance, rather than having one controller handle numerous conditional logics for creating or updating objects, we opted for presenters to consolidate responsibilities.
00:22:32.890
As we wrap up, I'd like to share that the slides for this talk are available on my blog, so feel free to check them out!